diff --git a/.github/workflows/issue_comment.yml b/.github/workflows/issue_comment.yml new file mode 100644 index 0000000000000..b5c80040fc9ff --- /dev/null +++ b/.github/workflows/issue_comment.yml @@ -0,0 +1,19 @@ +name: Sync issue comments to JIRA + +# This workflow will be triggered when new issue comment is created (including PR comments) +on: issue_comment + +jobs: + sync_issue_comments_to_jira: + name: Sync Issue Comments to Jira + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Sync issue comments to JIRA + uses: espressif/github-actions/sync_issues_to_jira@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JIRA_PASS: ${{ secrets.JIRA_PASS }} + JIRA_PROJECT: LLVM + JIRA_URL: ${{ secrets.JIRA_URL }} + JIRA_USER: ${{ secrets.JIRA_USER }} diff --git a/.github/workflows/new_issues.yml b/.github/workflows/new_issues.yml new file mode 100644 index 0000000000000..a6602d1c7aa1c --- /dev/null +++ b/.github/workflows/new_issues.yml @@ -0,0 +1,19 @@ +name: Sync issues to Jira + +# This workflow will be triggered when a new issue is opened +on: issues + +jobs: + sync_issues_to_jira: + name: Sync issues to Jira + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Sync GitHub issues to Jira project + uses: espressif/github-actions/sync_issues_to_jira@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JIRA_PASS: ${{ secrets.JIRA_PASS }} + JIRA_PROJECT: LLVM + JIRA_URL: ${{ secrets.JIRA_URL }} + JIRA_USER: ${{ secrets.JIRA_USER }} diff --git a/.github/workflows/new_prs.yml b/.github/workflows/new_prs.yml new file mode 100644 index 0000000000000..199d58ef87b3f --- /dev/null +++ b/.github/workflows/new_prs.yml @@ -0,0 +1,24 @@ +name: Sync remain PRs to Jira + +# This workflow will be triggered every hour, to sync remaining PRs (i.e. PRs with zero comment) to Jira project +# Note that, PRs can also get synced when new PR comment is created +on: + schedule: + - cron: "0 * * * *" + +jobs: + sync_prs_to_jira: + name: Sync PRs to Jira + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Sync PRs to Jira project + uses: espressif/github-actions/sync_issues_to_jira@master + with: + cron_job: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JIRA_PASS: ${{ secrets.JIRA_PASS }} + JIRA_PROJECT: LLVM + JIRA_URL: ${{ secrets.JIRA_URL }} + JIRA_USER: ${{ secrets.JIRA_USER }} diff --git a/.gitignore b/.gitignore index 20c4f52cd3786..0e13e97841618 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,9 @@ # Nested build directory /build* +/*/build-* +/_build +/_dist #==============================================================================# # Explicit files to ignore (only matches one). diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000..dbe33f2bd2c31 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,105 @@ +stages: + - build + - pack + - sign + - private_deploy + - test + - public_deploy + +image: ${CI_DOCKER_REGISTRY}/llvm-build:4 + +variables: + + # move all these to CI/CD settings + REL_SFX: "llvm" + CLANG_VER: "15.0.0" + GCC_REL_NAME: "esp-2022r1" + GCC_REL_VER: "gcc11_2_0" + NEWLIB_REF: "esp-2022r1" + BINUTILS_REF: "esp-2022r1-binutils" + XTENSA_OVERLAYS_REF: "master" + LLVM_GCC_TESTSUITE_REF: "esp-15.0.0-20221201" + XTENSA_CLANG_TOOLCHAIN_REF: "esp-15.0.0-20221201" + + CROSS_ARM_IMAGE: $CI_DOCKER_REGISTRY/llvm-build-cross-arm:1 + PLATFORM_NAME_LINUX: "linux-amd64" + PLATFORM_NAME_LINUX_ARMHF: "linux-armhf" + PLATFORM_NAME_LINUX_ARM64: "linux-arm64" + PLATFORM_NAME_WIN: "win64" + PLATFORM_NAME_MACOS: "macos" + PLATFORM_NAME_MACOS_ARM64: "macos-arm64" + + ARCHIVE_TOOL_LINUX: "tar -cJf" + UNARCHIVE_TOOL_LINUX: "tar -xf" + ARCHIVE_EXT_LINUX: "tar.xz" + + ARCHIVE_TOOL_WIN: "zip -9 -r" + UNARCHIVE_TOOL_WIN: "unzip" + ARCHIVE_EXT_WIN: "zip" + + PACK_ARCHIVE_TOOL_WIN: "tar -h -cJf" + PACK_UNARCHIVE_TOOL_WIN: "${UNARCHIVE_TOOL_LINUX}" + PACK_ARCHIVE_EXT_WIN: "${ARCHIVE_EXT_LINUX}" + + ARCHIVE_TOOL_MACOS: "tar -cJf" + UNARCHIVE_TOOL_MACOS: "tar -xf" + ARCHIVE_EXT_MACOS: "tar.xz" + + ARCHIVE_TOOL_NEWLIB: ${ARCHIVE_TOOL_LINUX} + UNARCHIVE_TOOL_NEWLIB: ${UNARCHIVE_TOOL_LINUX} + ARCHIVE_EXT_NEWLIB: ${ARCHIVE_EXT_LINUX} + + DIST_DIR: "dist" + BUILD_DIR: "build" + DOWNLOADS_DIR: "downloads" + +########################################################################### +#################### START OF TEMPORARY LEGACY CODE ####################### +# TODO: the code below is to be removed after migration to new build script + CONF_TARGET: "xtensa-esp32-elf" + XTENSA_CLANG_TOOLCHAIN: "${CONF_TARGET}-clang" +##################### END OF TEMPORARY LEGACY CODE ######################## +########################################################################### + +.use_ci_tools_snippet: &use_ci_tools_snippet | + curl -sSL ${CIT_LOADER_URL} -o cit_loader.sh && sh cit_loader.sh + source citools/import_functions + +.use_ci_tools: + script: + - *use_ci_tools_snippet + +.add_gitlab_key_snippet: &add_gitlab_key_snippet | + cit_add_ssh_key "${GITLAB_KEY}" + +.add_gitlab_key: + script: + - *add_gitlab_key_snippet + +# LLVM Build System used the remote address to show detailed version info, we'll change it to the public repository +.fix_origin_remote_for_public_snippet: &fix_origin_remote_for_public_snippet | + git remote set-url origin "${GH_REPO_HTTPS}" + +.fix_origin_remote_for_public: + script: + - *fix_origin_remote_for_public_snippet + +.get_clang_toolchain_build_scripts_snippet: &get_clang_toolchain_build_scripts_snippet | + git clone -b ${XTENSA_CLANG_TOOLCHAIN_REF} ${GITLAB_SSH_SERVER}/${XTENSA_CLANG_TOOLCHAIN_REPO} + cp -r xtensa-clang-toolchain/* . + +.get_clang_toolchain_build_scripts: + script: + - *get_clang_toolchain_build_scripts_snippet + +before_script: + - !reference [.use_ci_tools, script] + - !reference [.add_gitlab_key, script] + +include: + - local: .universal-toolchain-release.yml + rules: + - if: $ESP_CLANG_LEGACY_RELEASE != "true" + - local: .legacy-release.yml + rules: + - if: $ESP_CLANG_LEGACY_RELEASE == "true" diff --git a/.legacy-release.yml b/.legacy-release.yml new file mode 100644 index 0000000000000..2042369e9049d --- /dev/null +++ b/.legacy-release.yml @@ -0,0 +1,164 @@ + +.get_release_name_legacy: &get_release_name_legacy | + # using annotated tags + REL_NUM=$(git describe --abbrev=7) + REL_SFX="llvm15_0_0" + REL_NAME=${CONF_TARGET}-${REL_SFX}-${REL_NUM}-${PLATFORM_NAME} + ARCHIVE_NAME=${REL_NAME}.${ARCHIVE_EXT} + echo "PLATFORM_NAME: $PLATFORM_NAME" + echo "REL_NUM: $REL_NUM" + echo "REL_NAME: $REL_NAME" + echo "ARCHIVE_NAME: $ARCHIVE_NAME" + +.get_gcc_toolchain_legacy: &get_gcc_toolchain_legacy | + wget --no-verbose https://dl.espressif.com/github_assets/espressif/crosstool-NG/releases/download/esp-2021r2-patch3/${XTENSA_GCC_TOOLCHAIN} + ${UNARCHIVE_TOOL} ${XTENSA_GCC_TOOLCHAIN} + if [[ "$XTENSA_GCC_TOOLCHAIN" == *"linux-amd64"* ]]; then + cp -r xtensa-esp32-elf ${XTENSA_CLANG_TOOLCHAIN} + else + mv xtensa-esp32-elf ${XTENSA_CLANG_TOOLCHAIN} + wget --no-verbose https://dl.espressif.com/github_assets/espressif/crosstool-NG/releases/download/esp-2021r2-patch3/xtensa-esp32-elf-${GCC_REL_NAME}-linux-amd64.tar.gz + tar -xf xtensa-esp32-elf-${GCC_REL_NAME}-linux-amd64.tar.gz + fi + export GCC_ESP32_LINUX_TOOLCHAIN="xtensa-esp32-elf" + +.package_toolchain_legacy: &package_toolchain_legacy | + ${ARCHIVE_TOOL} ${ARCHIVE_NAME} ${XTENSA_CLANG_TOOLCHAIN}/ + mkdir -p ${DIST_DIR} + mv ${ARCHIVE_NAME} ${DIST_DIR}/ + echo "${ARCHIVE_NAME}" > ${DIST_DIR}/file_${PLATFORM_NAME}_${CONF_TARGET} + +.build_template_legacy: + stage: build + tags: [ "amd64", "build" ] + artifacts: + paths: + - ${DIST_DIR}/ + when: always + expire_in: 10 day + variables: + XTENSA_CLANG_TOOLCHAIN_REF: "release_esp32_clang_15.0.0_gcc_8.4.0" + GCC_REL_NAME: "gcc8_4_0-esp-2021r2-patch3" + script: + - *get_release_name_legacy + - *get_gcc_toolchain_legacy + - !reference [.fix_origin_remote_for_public, script] + - !reference [.get_clang_toolchain_build_scripts, script] + - ${BUILD_TOOLCHAIN_CMD} "${XTENSA_CLANG_TOOLCHAIN}" + - *package_toolchain_legacy + +linux_amd64_build: + extends: .build_template_legacy + variables: + PLATFORM_NAME: "${PLATFORM_NAME_LINUX}" + ARCHIVE_TOOL: "${ARCHIVE_TOOL_LINUX}" + UNARCHIVE_TOOL: "${UNARCHIVE_TOOL_LINUX}" + ARCHIVE_EXT: "${ARCHIVE_EXT_LINUX}" + XTENSA_GCC_TOOLCHAIN: "xtensa-esp32-elf-${GCC_REL_NAME}-linux-amd64.tar.gz" + BUILD_TOOLCHAIN_CMD: "./build-toolchain-linux.sh" + +linux_arm64_build: + extends: .build_template_legacy + image: ${CROSS_ARM_IMAGE} + variables: + PLATFORM_NAME: "${PLATFORM_NAME_LINUX_ARM64}" + ARCHIVE_TOOL: "${ARCHIVE_TOOL_LINUX}" + UNARCHIVE_TOOL: "${UNARCHIVE_TOOL_LINUX}" + ARCHIVE_EXT: "${ARCHIVE_EXT_LINUX}" + XTENSA_GCC_TOOLCHAIN: "xtensa-esp32-elf-${GCC_REL_NAME}-linux-arm64.tar.gz" + BUILD_TOOLCHAIN_CMD: "./build-toolchain-linux-arm64.sh" + +win64_build: + extends: .build_template_legacy + variables: + PLATFORM_NAME: "${PLATFORM_NAME_WIN}" + ARCHIVE_TOOL: "${ARCHIVE_TOOL_WIN}" + UNARCHIVE_TOOL: "${UNARCHIVE_TOOL_WIN}" + ARCHIVE_EXT: "${ARCHIVE_EXT_WIN}" + XTENSA_GCC_TOOLCHAIN: "xtensa-esp32-elf-${GCC_REL_NAME}-win64.zip" + BUILD_TOOLCHAIN_CMD: "./build-toolchain-win.sh" + +macos_amd64_build: + extends: .build_template_legacy + variables: + PLATFORM_NAME: "${PLATFORM_NAME_MACOS}" + ARCHIVE_TOOL: "${ARCHIVE_TOOL_MACOS}" + UNARCHIVE_TOOL: "${UNARCHIVE_TOOL_MACOS}" + ARCHIVE_EXT: "${ARCHIVE_EXT_MACOS}" + XTENSA_GCC_TOOLCHAIN: "xtensa-esp32-elf-${GCC_REL_NAME}-macos.tar.gz" + BUILD_TOOLCHAIN_CMD: "./build-toolchain-macos.sh" + +linux_amd64_testsuite: + stage: test + tags: [ "amd64", "build" ] + needs: + - job: linux_amd64_build + variables: + PLATFORM_NAME: "${PLATFORM_NAME_LINUX}" + ARCHIVE_TOOL: "${ARCHIVE_TOOL_LINUX}" + UNARCHIVE_TOOL: "${UNARCHIVE_TOOL_LINUX}" + ARCHIVE_EXT: "${ARCHIVE_EXT_LINUX}" + LLVM_GCC_TESTSUITE_REF: "feature/ci_llvm_multitarget_crt_tests" + script: + - *get_release_name_legacy + - ${UNARCHIVE_TOOL} ${DIST_DIR}/${ARCHIVE_NAME} + + # getting testsuite + - git clone -b ${LLVM_GCC_TESTSUITE_REF} --depth 1 $GITLAB_SSH_SERVER/idf/${LLVM_TESTSUITE_REPO}.git + + # preparing testsuite + - export PATH=${PWD}/${XTENSA_CLANG_TOOLCHAIN}/bin/:$PATH + - cd ${LLVM_TESTSUITE_REPO} + + # qemu + - ./qemu_esp32_install.sh + + # run testsuite for esp32 + - ./run_esp32_tests.sh + + # run testsuite for compiler_rt library + - ./run_esp32_crt_tests.sh ../$XTENSA_CLANG_TOOLCHAIN + +upload_to_http_legacy: + stage: private_deploy + when: manual + allow_failure: true + tags: [ "deploy", "shiny" ] + variables: + # force the fetch strategy to clean old archives up in dist/ dir + GIT_STRATEGY: fetch + before_script: + - !reference [.use_ci_tools, script] + script: + - cit_add_ssh_key "${HTTP_UPLOAD_KEY}" + # List of archives + - FILES=$(find ${DIST_DIR} -name file_\* -exec cat {} \+) + - cd ${DIST_DIR} + - scp ${FILES} ${HTTP_UPLOAD_DIR}/ct-ng/llvm-builds + # Show info + - echo -e "\nArchives were published there:\n\n$(for n in ${FILES}; do echo "${HTTP_PUBLIC_DIR}/ct-ng/llvm-builds/${n}"; done)\n" + +upload_to_github_legacy: + stage: public_deploy + when: manual + allow_failure: true + only: + - tags + tags: [ "amd64", "internet" ] + image: espressif/github-hub:2 + variables: + GIT_STRATEGY: fetch + GITHUB_TOKEN: "${GH_TOKEN}" + GITHUB_REPO: "${GH_REPO_HTTPS}" + TAG: "${CI_COMMIT_TAG}" + before_script: [] + script: + - ls -l dist*/ + - git remote add github ${GH_REPO_HTTPS} + - hub release show ${TAG} || { echo "Please create a release on GitHub with ${TAG} tag at first"; exit 1; } + # List of archives + - FILES=$(find ${DIST_DIR} -name file_\* -exec cat {} \+) + - cd ${DIST_DIR} + - ls -l $FILES + # Upload archives + - for n in ${FILES}; do hub release edit -m "" -a "${n}" "${TAG}"; done diff --git a/.universal-toolchain-release.yml b/.universal-toolchain-release.yml new file mode 100644 index 0000000000000..75a41258aead0 --- /dev/null +++ b/.universal-toolchain-release.yml @@ -0,0 +1,464 @@ + +# Prepare release name/number +.get_release_name: &get_release_name | + # using annotated tags + REL_NUM=$(git describe --abbrev=7) + REL_NAME=${REL_SFX}-${REL_NUM}-${PLATFORM_NAME} + ARCHIVE_NAME=${REL_NAME}.${ARCHIVE_EXT} + LIBS_ARCHIVE_NAME=libs_${REL_NAME}.${ARCHIVE_EXT} + echo "PLATFORM_NAME: $PLATFORM_NAME" + echo "REL_NUM: $REL_NUM" + echo "REL_NAME: $REL_NAME" + echo "ARCHIVE_NAME: $ARCHIVE_NAME" + +# Get an existing crosstool-ng builds for all chips +.get_gcc_toolchain: &get_gcc_toolchain | + declare -a XTENSA_CPUS=("esp32" + "esp32s2" + "esp32s3") + for ((i = 0; i < ${#XTENSA_CPUS[@]}; i++)); do + XTENSA_CPU=${XTENSA_CPUS[$i]} + GCC_TOOLCHAIN_ARCH=xtensa-${XTENSA_CPU}-elf-${GCC_REL_VER}-${GCC_REL_NAME}-${PLATFORM_NAME}.${GCC_ARCHIVE_EXT} + wget --no-verbose https://dl.espressif.com/github_assets/espressif/crosstool-NG/releases/download/${GCC_REL_NAME}/${GCC_TOOLCHAIN_ARCH} + ${GCC_UNARCHIVE_TOOL} ${GCC_TOOLCHAIN_ARCH} + done; + GCC_TOOLCHAIN_ARCH=riscv32-esp-elf-${GCC_REL_VER}-${GCC_REL_NAME}-${PLATFORM_NAME}.${GCC_ARCHIVE_EXT} + wget --no-verbose https://dl.espressif.com/github_assets/espressif/crosstool-NG/releases/download/${GCC_REL_NAME}/${GCC_TOOLCHAIN_ARCH} + ${GCC_UNARCHIVE_TOOL} ${GCC_TOOLCHAIN_ARCH} + +# Pack the toolchain +.package_toolchain: &package_toolchain | + ${ARCHIVE_TOOL} ${ARCHIVE_NAME} esp-clang/ + mkdir -p ${DISTRO_DIR} + mv ${ARCHIVE_NAME} ${DISTRO_DIR}/ + echo "${ARCHIVE_NAME}" > ${DISTRO_DIR}/file_${PLATFORM_NAME} + +# Pack libs to be used for Rust, Go etc. +.package_libs: &package_libs | + eval ${ARCHIVE_TOOL} ${LIBS_ARCHIVE_NAME} esp-clang/lib/clang/${CLANG_VER}/include esp-clang/lib/lib{clang,LLVM}* ${LIBS_PACK_EXTRA_PATHS:-} + mkdir -p ${DISTRO_DIR} + mv ${LIBS_ARCHIVE_NAME} ${DISTRO_DIR}/ + echo "${LIBS_ARCHIVE_NAME}" > ${DISTRO_DIR}/file_libs-${PLATFORM_NAME} + +.get_binutils: &get_binutils | + git clone -b ${BINUTILS_REF} --single-branch ${GITLAB_SSH_SERVER}/idf/${BINUTILS_REPO}.git + BINUTILS_PATH=$PWD/${BINUTILS_REPO} + +.get_xtensa_overlays: &get_xtensa_overlays | + git clone -b ${XTENSA_OVERLAYS_REF} --single-branch ${GITLAB_SSH_SERVER}/idf/${XTENSA_OVERLAYS_REPO}.git + XTENSA_OVERLAYS_PATH=$PWD/${XTENSA_OVERLAYS_REPO} + +.get_newlib: &get_newlib | + git clone -b ${NEWLIB_REF} --single-branch ${GITLAB_SSH_SERVER}/idf/${NEWLIB_REPO}.git + NEWLIB_PATH=$PWD/${NEWLIB_REPO} + +.build_template: + stage: build + tags: [ "amd64", "build" ] + artifacts: + paths: + - ${DIST_DIR}/ + - ${BUILD_DIR}/lld-tests.log + - ${BUILD_DIR}/tests.log + - ${BUILD_DIR}/build.log + when: always + expire_in: 1 day + variables: + BUILD_TOOLCHAIN_CMD_EXTRA_ARGS: "" + after_script: + # help to identify that build failed due to OOM + - > + if [ $CI_JOB_STATUS == 'failed' ]; then + [ ! -f "${BUILD_DIR}/build.log" ] || grep -i "internal compiler error\|Killed" ${BUILD_DIR}/build.log || true + [ ! -f "${BUILD_DIR}/tests.log" ] || grep -i "internal compiler error\|Killed" ${BUILD_DIR}/tests.log || true + [ ! -f "${BUILD_DIR}/lld-tests.log" ] || grep -i "internal compiler error\|Killed" ${BUILD_DIR}/lld-tests.log || true + fi + script: + - *get_release_name + - mkdir ${DOWNLOADS_DIR} + - pushd ${DOWNLOADS_DIR} + - ESP_GCC_TOOLCHAIN_DIST_BASE=$PWD + - *get_gcc_toolchain + - *get_binutils + - *get_xtensa_overlays + - popd + - !reference [.get_clang_toolchain_build_scripts, script] + - !reference [.fix_origin_remote_for_public, script] + - LLVM_PROJECT_PATH=$PWD + - BUILD_PATH=$PWD/${BUILD_DIR} + - mkdir -p ${BUILD_PATH} + - export USE_PARALLEL_LINK_JOBS=2 + # build Clang toolchain w/o newlib + - ${BUILD_TOOLCHAIN_CMD} --llvm-path=${LLVM_PROJECT_PATH} + --gcc-toolchains-path=${ESP_GCC_TOOLCHAIN_DIST_BASE} --binutils-path=${BINUTILS_PATH} + --xtensa-overlays-path=${XTENSA_OVERLAYS_PATH} --host=${CONF_HOST} ${BUILD_TOOLCHAIN_CMD_EXTRA_ARGS} ${BUILD_PATH} 2>&1 > ${BUILD_PATH}/build.log + - BUILD_HOST=$(gcc -dumpmachine) + # Do not run unit tests for cross-builds. + # Run as non-root user because permission tests fail when run by root. + - if [ "${CONF_HOST}" == "${BUILD_HOST}" ]; then + export LLVM_BUILD_PATH=${LLVM_PROJECT_PATH}/llvm/build-${CONF_HOST}-Release; + echo "Run unit tests for native build in ${LLVM_BUILD_PATH}"; + useradd -m test_runner; + chown -R test_runner ${LLVM_BUILD_PATH}; + touch ${BUILD_PATH}/tests.log; + chmod o+w ${BUILD_PATH}/tests.log; + runuser -l test_runner -c 'cmake --build '${LLVM_BUILD_PATH}' --target check-all 2>&1 > '${BUILD_PATH}'/tests.log'; + touch ${BUILD_PATH}/lld-tests.log; + chmod o+w ${BUILD_PATH}/lld-tests.log; + runuser -l test_runner -c 'cmake --build '${LLVM_BUILD_PATH}' --target lld-test 2>&1 > '${BUILD_PATH}'/lld-tests.log'; + fi + - export DISTRO_DIR=$PWD/$DIST_DIR + - pushd ${BUILD_PATH} + - *package_toolchain + - popd + +.build_linux-gnu_template: + extends: .build_template + variables: + ARCHIVE_TOOL: "${ARCHIVE_TOOL_LINUX}" + ARCHIVE_EXT: "${ARCHIVE_EXT_LINUX}" + GCC_UNARCHIVE_TOOL: "${UNARCHIVE_TOOL_LINUX}" + GCC_ARCHIVE_EXT: "${ARCHIVE_EXT_LINUX}" + BUILD_TOOLCHAIN_CMD: "./build-toolchain.sh" + +build_x86_64-linux-gnu: + extends: .build_linux-gnu_template + variables: + CONF_HOST: "x86_64-linux-gnu" + PLATFORM_NAME: "${PLATFORM_NAME_LINUX}" + +build_arm-linux-gnueabihf: + extends: .build_linux-gnu_template + image: ${CROSS_ARM_IMAGE} + variables: + CONF_HOST: "arm-linux-gnueabihf" + PLATFORM_NAME: "${PLATFORM_NAME_LINUX_ARMHF}" + +build_aarch64-linux-gnu: + extends: .build_linux-gnu_template + image: ${CROSS_ARM_IMAGE} + variables: + CONF_HOST: "aarch64-linux-gnu" + PLATFORM_NAME: "${PLATFORM_NAME_LINUX_ARM64}" + +build_x86_64-w64-mingw32: + extends: .build_template + needs: + # needs native toolchain and newlib from this job + - job: build_x86_64-linux-gnu + before_script: + - !reference [.use_ci_tools, script] + - !reference [.add_gitlab_key, script] + # get ARCHIVE_NAME for Linux release. Modify vars to make get_release_name working properly + - CLANG_LINUX_ARCHIVE=$(cat ${DIST_DIR}/file_${PLATFORM_NAME_LINUX}) + # unpack x86_64-linux-gnu toolchain to re-use it as native Clang for Windows build + - mkdir -p esp-clang-${PLATFORM_NAME_LINUX} + - ${UNARCHIVE_TOOL_LINUX} ${DIST_DIR}/${CLANG_LINUX_ARCHIVE} -C esp-clang-${PLATFORM_NAME_LINUX} + # we do not want to keep artifacts from 'x86_64-linux-gnu' job + - rm -rf ${DIST_DIR} + - rm -rf ${BUILD_DIR} + # add build command args speciifc for Windows build + - export BUILD_TOOLCHAIN_CMD_EXTRA_ARGS="--native-esp-clang-path=$PWD/esp-clang-${PLATFORM_NAME_LINUX}" + variables: + CONF_HOST: "x86_64-w64-mingw32" + PLATFORM_NAME: "${PLATFORM_NAME_WIN}" + ARCHIVE_TOOL: "${ARCHIVE_TOOL_LINUX}" + ARCHIVE_EXT: "${ARCHIVE_EXT_LINUX}" + GCC_UNARCHIVE_TOOL: "${UNARCHIVE_TOOL_WIN}" + GCC_ARCHIVE_EXT: "${ARCHIVE_EXT_WIN}" + BUILD_TOOLCHAIN_CMD: "./build-toolchain-win.sh" + +.build_apple-darwin_template: + extends: .build_template + variables: + ARCHIVE_TOOL: "${ARCHIVE_TOOL_MACOS}" + ARCHIVE_EXT: "${ARCHIVE_EXT_MACOS}" + GCC_UNARCHIVE_TOOL: "${UNARCHIVE_TOOL_MACOS}" + GCC_ARCHIVE_EXT: "${ARCHIVE_EXT_MACOS}" + BUILD_TOOLCHAIN_CMD: "./build-toolchain.sh" + +build_x86_64-apple-darwin: + extends: .build_apple-darwin_template + variables: + CONF_HOST: "x86_64-apple-darwin21.1" + PLATFORM_NAME: "${PLATFORM_NAME_MACOS}" + +build_aarch64-apple-darwin: + extends: .build_apple-darwin_template + variables: + CONF_HOST: "aarch64-apple-darwin21.1" + PLATFORM_NAME: "${PLATFORM_NAME_MACOS_ARM64}" + +build_newlib: + stage: build + tags: [ "amd64", "build" ] + needs: + # needs native toolchainfrom this job + - job: build_x86_64-linux-gnu + artifacts: + paths: + - ${DIST_DIR}/ + - ${BUILD_DIR}/build.log + when: always + expire_in: 1 day + variables: + PLATFORM_NAME: "${PLATFORM_NAME_LINUX}" + ARCHIVE_TOOL: "${ARCHIVE_TOOL_LINUX}" + UNARCHIVE_TOOL: "${UNARCHIVE_TOOL_LINUX}" + ARCHIVE_EXT: "${ARCHIVE_EXT_LINUX}" + script: + # get ARCHIVE_NAME for Linux release. + - CLANG_ARCHIVE=$PWD/${DIST_DIR}/$(cat ${DIST_DIR}/file_${PLATFORM_NAME_LINUX}) + - mkdir -p ${DOWNLOADS_DIR} + - pushd ${DOWNLOADS_DIR} + - *get_xtensa_overlays + - *get_newlib + # unpack clang + - ${UNARCHIVE_TOOL} ${CLANG_ARCHIVE} + - export PATH=$PWD/esp-clang/bin:$PATH + - popd + - rm -rf $PWD/${DIST_DIR} + - !reference [.get_clang_toolchain_build_scripts, script] + # build newlib overlay using ESP native (Linux) clang toolchain only + # it will be re-used for cross-buit toolchains (win and mac). + - NEWLIB_OVERLAY_DISTRO_PATH=$PWD/${DIST_DIR} + - mkdir -p ${NEWLIB_OVERLAY_DISTRO_PATH} + - BUILD_PATH=$PWD/${BUILD_DIR} + - mkdir -p ${BUILD_PATH} + - ./build-toolchain.sh --newlib-path=${NEWLIB_PATH} --xtensa-overlays-path=${XTENSA_OVERLAYS_PATH} ${BUILD_PATH} 2>&1 > ${BUILD_PATH}/build.log + - pushd ${BUILD_PATH} + - ${ARCHIVE_TOOL_NEWLIB} ${NEWLIB_OVERLAY_DISTRO_PATH}/esp-clang-newlib-overlay.${ARCHIVE_EXT_NEWLIB} esp-clang/ + - popd + +.pack_template: + stage: pack + tags: [ "amd64", "build" ] + artifacts: + paths: + - ${DIST_DIR}/ + when: always + expire_in: 3 day + script: + - *get_release_name + - export BUILD_PATH=$PWD/${BUILD_DIR} + - mkdir -p ${BUILD_PATH} + # unpack clang + - ${UNARCHIVE_TOOL} ${DIST_DIR}/${ARCHIVE_NAME} -C ${BUILD_PATH} + # unpack newlib + - ${UNARCHIVE_TOOL_NEWLIB} ${DIST_DIR}/esp-clang-newlib-overlay.${ARCHIVE_EXT_NEWLIB} -C ${BUILD_PATH} + - rm -rf ${DIST_DIR} + - !reference [.get_clang_toolchain_build_scripts, script] + # strip binutils afer newlib is built + - STRIP_BINUTILS=YES ./build-toolchain.sh --host=${CONF_HOST} ${BUILD_PATH} + - DISTRO_DIR=$PWD/${DIST_DIR} + - pushd ${BUILD_PATH} + - *package_toolchain + - *package_libs + - popd + +.pack_linux-gnu_template: + extends: .pack_template + variables: + ARCHIVE_TOOL: "${ARCHIVE_TOOL_LINUX}" + UNARCHIVE_TOOL: "${UNARCHIVE_TOOL_LINUX}" + ARCHIVE_EXT: "${ARCHIVE_EXT_LINUX}" + +pack_x86_64-linux-gnu: + extends: .pack_linux-gnu_template + needs: + - job: build_x86_64-linux-gnu + - job: build_newlib + variables: + CONF_HOST: "x86_64-linux-gnu" + PLATFORM_NAME: "${PLATFORM_NAME_LINUX}" + +pack_arm-linux-gnueabihf: + extends: .pack_linux-gnu_template + image: ${CROSS_ARM_IMAGE} + needs: + - job: build_arm-linux-gnueabihf + - job: build_newlib + variables: + CONF_HOST: "arm-linux-gnueabihf" + PLATFORM_NAME: "${PLATFORM_NAME_LINUX_ARMHF}" + +pack_aarch64-linux-gnu: + extends: .pack_linux-gnu_template + image: ${CROSS_ARM_IMAGE} + needs: + - job: build_aarch64-linux-gnu + - job: build_newlib + variables: + CONF_HOST: "aarch64-linux-gnu" + PLATFORM_NAME: "${PLATFORM_NAME_LINUX_ARM64}" + +pack_x86_64-w64-mingw32: + extends: .pack_template + needs: + - job: build_x86_64-w64-mingw32 + - job: build_newlib + variables: + CONF_HOST: "x86_64-w64-mingw32" + PLATFORM_NAME: "${PLATFORM_NAME_WIN}" + ARCHIVE_TOOL: "${PACK_ARCHIVE_TOOL_WIN}" + UNARCHIVE_TOOL: "${PACK_UNARCHIVE_TOOL_WIN}" + ARCHIVE_EXT: "${PACK_ARCHIVE_EXT_WIN}" + LIBS_PACK_EXTRA_PATHS: esp-clang/bin/lib{c++,clang,LLVM,unwind}* + +.pack_apple-darwin_template: + extends: .pack_template + variables: + ARCHIVE_TOOL: "${ARCHIVE_TOOL_MACOS}" + UNARCHIVE_TOOL: "${UNARCHIVE_TOOL_MACOS}" + ARCHIVE_EXT: "${ARCHIVE_EXT_MACOS}" + +pack_x86_64-apple-darwin: + extends: .pack_apple-darwin_template + needs: + - job: build_x86_64-apple-darwin + - job: build_newlib + variables: + CONF_HOST: "x86_64-apple-darwin21.1" + PLATFORM_NAME: "${PLATFORM_NAME_MACOS}" + +pack_aarch64-apple-darwin: + extends: .pack_apple-darwin_template + needs: + - job: build_aarch64-apple-darwin + - job: build_newlib + variables: + CONF_HOST: "aarch64-apple-darwin21.1" + PLATFORM_NAME: "${PLATFORM_NAME_MACOS_ARM64}" + +test_x86_64-linux-gnu: + stage: test + tags: [ "amd64", "build" ] + needs: + - job: pack_x86_64-linux-gnu + variables: + PLATFORM_NAME: "${PLATFORM_NAME_LINUX}" + ARCHIVE_TOOL: "${ARCHIVE_TOOL_LINUX}" + UNARCHIVE_TOOL: "${UNARCHIVE_TOOL_LINUX}" + ARCHIVE_EXT: "${ARCHIVE_EXT_LINUX}" + script: + - *get_release_name + - ${UNARCHIVE_TOOL} ${DIST_DIR}/${ARCHIVE_NAME} + # getting testsuite + - git clone -b ${LLVM_GCC_TESTSUITE_REF} --depth 1 $GITLAB_SSH_SERVER/idf/${LLVM_TESTSUITE_REPO}.git + # preparing testsuite + - export PATH=${PWD}/esp-clang/bin:$PATH + - cd ${LLVM_TESTSUITE_REPO} + # qemu + - ./qemu_esp32_install.sh + # run testsuite for esp32 + - ./run_esp32_tests.sh + +.macos_codesign: &macos_codesign + stage: sign + tags: [ "darwin", "amd64" ] + resource_group: macos_codesign + artifacts: + paths: + - ${DIST_DIR}/ + when: always + expire_in: 3 day + variables: + KEYCHAIN_NAME: "llvm.keychain" + ARCHIVE_TOOL: "${ARCHIVE_TOOL_MACOS}" + UNARCHIVE_TOOL: "${UNARCHIVE_TOOL_MACOS}" + ARCHIVE_EXT: "${ARCHIVE_EXT_MACOS}" + script: + - *get_release_name + - ${UNARCHIVE_TOOL} ${DIST_DIR}/${ARCHIVE_NAME} + - rm -rf ${DIST_DIR} + - TOOLCHAIN_PATH=$PWD/esp-clang + - echo $MACOS_CERTIFICATE | base64 --decode > $PWD/certificate.p12 + - security create-keychain -p $KEYCHAIN_PWD $KEYCHAIN_NAME || true + - security import $PWD/certificate.p12 -k $KEYCHAIN_NAME -P $MACOS_CERTIFICATE_PWD -T /usr/bin/codesign + - security set-key-partition-list -S apple-tool:,apple:,codesign -s -k $KEYCHAIN_PWD $KEYCHAIN_NAME + - security list-keychains -d user -s ~/Library/Keychains/$KEYCHAIN_NAME + - security find-identity -v -p codesigning + - security unlock-keychain -p $KEYCHAIN_PWD $KEYCHAIN_NAME + - /usr/bin/codesign -v --force --options runtime -s $IDENTITY_ID $TOOLCHAIN_PATH/bin/* $TOOLCHAIN_PATH/lib/*.dylib + - security delete-keychain $KEYCHAIN_NAME + - codesign -dvv $TOOLCHAIN_PATH/bin/* + - DISTRO_DIR=$PWD/${DIST_DIR} + - *package_toolchain + - *package_libs + after_script: + - security find-identity -v + - security delete-keychain $KEYCHAIN_NAME + - security find-identity -v + +sign_x86_64-apple-darwin: + extends: .macos_codesign + needs: + - pack_x86_64-apple-darwin + variables: + PLATFORM_NAME: "${PLATFORM_NAME_MACOS}" + +sign_aarch64-apple-darwin: + extends: .macos_codesign + needs: + - pack_aarch64-apple-darwin + variables: + PLATFORM_NAME: "${PLATFORM_NAME_MACOS_ARM64}" + +upload_to_http: + stage: private_deploy + when: manual + allow_failure: true + tags: [ "deploy", "shiny" ] + variables: + # force the fetch strategy to clean old archives up in dist/ dir + GIT_STRATEGY: fetch + needs: + - job: pack_x86_64-linux-gnu + - job: pack_arm-linux-gnueabihf + - job: pack_aarch64-linux-gnu + - job: pack_x86_64-w64-mingw32 + - job: sign_x86_64-apple-darwin + - job: sign_aarch64-apple-darwin + before_script: + - !reference [.use_ci_tools, script] + script: + - cit_add_ssh_key "${HTTP_UPLOAD_KEY}" + # List of archives + - FILES=$(find ${DIST_DIR} -name file_\* -exec cat {} \+) + - cd ${DIST_DIR} + - ls -l $FILES + - scp ${FILES} ${HTTP_UPLOAD_DIR}/ct-ng/llvm-builds + # Show info + - echo -e "\nArchives were published there:\n\n$(for n in ${FILES}; do echo "${HTTP_PUBLIC_DIR}/ct-ng/llvm-builds/${n}"; done)\n" + +upload_to_github: + stage: public_deploy + when: manual + allow_failure: true + only: + - tags + tags: [ "amd64", "internet" ] + image: espressif/github-hub:2 + variables: + GIT_STRATEGY: fetch + GITHUB_TOKEN: "${GH_TOKEN}" + GITHUB_REPO: "${GH_REPO_HTTPS}" + TAG: "${CI_COMMIT_TAG}" + needs: + - job: pack_x86_64-linux-gnu + - job: pack_arm-linux-gnueabihf + - job: pack_aarch64-linux-gnu + - job: pack_x86_64-w64-mingw32 + - job: sign_x86_64-apple-darwin + - job: sign_aarch64-apple-darwin + before_script: [] + script: + - ls -l dist*/ + - git remote add github ${GH_REPO_HTTPS} + - hub release show ${TAG} || { echo "Please create a release on GitHub with ${TAG} tag at first"; exit 1; } + # List of archives + - FILES=$(find ${DIST_DIR} -name file_\* -exec cat {} \+) + - cd ${DIST_DIR} + - ls -l $FILES + # Upload archives + - for n in ${FILES}; do hub release edit -m "" -a "${n}" "${TAG}"; done diff --git a/0001-Xtensa-Improve-fixup-error-messages-in-asm-backend.patch b/0001-Xtensa-Improve-fixup-error-messages-in-asm-backend.patch new file mode 100644 index 0000000000000..a7b3a23890e40 --- /dev/null +++ b/0001-Xtensa-Improve-fixup-error-messages-in-asm-backend.patch @@ -0,0 +1,68 @@ +From c8f321e8f88802dd427bc9772da171d5c889a77d Mon Sep 17 00:00:00 2001 +From: Andrei Safronov +Date: Thu, 9 Mar 2023 10:30:04 +0300 +Subject: [PATCH] [Xtensa] Improve fixup error messages in asm backend. + +--- + .../Xtensa/MCTargetDesc/XtensaAsmBackend.cpp | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaAsmBackend.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaAsmBackend.cpp +index 4039804b1b58..6981c82e768f 100644 +--- a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaAsmBackend.cpp ++++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaAsmBackend.cpp +@@ -93,7 +93,7 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, + case Xtensa::fixup_xtensa_branch_6: { + Value -= 4; + if (!isInt<6>(Value)) +- Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); ++ Ctx.reportError(Fixup.getLoc(), "branch 6-bit fixup value out is of range"); + unsigned Hi2 = (Value >> 4) & 0x3; + unsigned Lo4 = Value & 0xf; + return (Hi2 << 4) | (Lo4 << 12); +@@ -101,36 +101,36 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, + case Xtensa::fixup_xtensa_branch_8: + Value -= 4; + if (!isInt<8>(Value)) +- Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); ++ Ctx.reportError(Fixup.getLoc(), "branch 8-bit fixup value out of range"); + return (Value & 0xff); + case Xtensa::fixup_xtensa_branch_12: + Value -= 4; + if (!isInt<12>(Value)) +- Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); ++ Ctx.reportError(Fixup.getLoc(), "branch 12-bit fixup value out of range"); + return (Value & 0xfff); + case Xtensa::fixup_xtensa_jump_18: + Value -= 4; + if (!isInt<18>(Value)) +- Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); ++ Ctx.reportError(Fixup.getLoc(), "jump fixup value out of range"); + return (Value & 0x3ffff); + case Xtensa::fixup_xtensa_call_18: + Value -= 4; + if (!isInt<20>(Value)) +- Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); ++ Ctx.reportError(Fixup.getLoc(), "call fixup value out of range"); + if (Value & 0x3) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned"); + return (Value & 0xffffc) >> 2; + case Xtensa::fixup_xtensa_loop_8: + Value -= 4; + if (!isUInt<8>(Value)) +- Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); ++ Ctx.reportError(Fixup.getLoc(), "loop fixup value out of range"); + return (Value & 0xff); + case Xtensa::fixup_xtensa_l32r_16: + unsigned Offset = Fixup.getOffset(); + if (Offset & 0x3) + Value -= 4; + if (!isInt<18>(Value) && (Value & 0x20000)) +- Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); ++ Ctx.reportError(Fixup.getLoc(), "l32r fixup value out of range"); + if (Value & 0x3) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned"); + return (Value & 0x3fffc) >> 2; +-- +2.25.1 + diff --git a/clang/include/clang/Basic/BuiltinsXtensa.def b/clang/include/clang/Basic/BuiltinsXtensa.def new file mode 100644 index 0000000000000..47f75f1665b16 --- /dev/null +++ b/clang/include/clang/Basic/BuiltinsXtensa.def @@ -0,0 +1,127 @@ +//===-- BuiltinsXtensa.def - Xtensa Builtin function database ----*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the Xtensa-specific builtin function database. Users of +// this file must define the BUILTIN macro to make use of this information. +// +//===----------------------------------------------------------------------===// + +// The format of this database matches clang/Basic/Builtins.def. + +BUILTIN(__builtin_xtensa_umul_aa_ll, "vUiUi", "n") +BUILTIN(__builtin_xtensa_umul_aa_lh, "vUiUi", "n") +BUILTIN(__builtin_xtensa_umul_aa_hl, "vUiUi", "n") +BUILTIN(__builtin_xtensa_umul_aa_hh, "vUiUi", "n") + +BUILTIN(__builtin_xtensa_mul_aa_ll, "vUiUi", "n") +BUILTIN(__builtin_xtensa_mul_aa_lh, "vUiUi", "n") +BUILTIN(__builtin_xtensa_mul_aa_hl, "vUiUi", "n") +BUILTIN(__builtin_xtensa_mul_aa_hh, "vUiUi", "n") + +BUILTIN(__builtin_xtensa_mul_ad_ll, "vUiIUi", "n") +BUILTIN(__builtin_xtensa_mul_ad_lh, "vUiIUi", "n") +BUILTIN(__builtin_xtensa_mul_ad_hl, "vUiIUi", "n") +BUILTIN(__builtin_xtensa_mul_ad_hh, "vUiIUi", "n") + +BUILTIN(__builtin_xtensa_mul_da_ll, "vIUiUi", "n") +BUILTIN(__builtin_xtensa_mul_da_lh, "vIUiUi", "n") +BUILTIN(__builtin_xtensa_mul_da_hl, "vIUiUi", "n") +BUILTIN(__builtin_xtensa_mul_da_hh, "vIUiUi", "n") + +BUILTIN(__builtin_xtensa_mul_dd_ll, "vIUiIUi", "n") +BUILTIN(__builtin_xtensa_mul_dd_lh, "vIUiIUi", "n") +BUILTIN(__builtin_xtensa_mul_dd_hl, "vIUiIUi", "n") +BUILTIN(__builtin_xtensa_mul_dd_hh, "vIUiIUi", "n") + +BUILTIN(__builtin_xtensa_mula_aa_ll, "vUiUi", "n") +BUILTIN(__builtin_xtensa_mula_aa_lh, "vUiUi", "n") +BUILTIN(__builtin_xtensa_mula_aa_hl, "vUiUi", "n") +BUILTIN(__builtin_xtensa_mula_aa_hh, "vUiUi", "n") + +BUILTIN(__builtin_xtensa_mula_ad_ll, "vUiIUi", "n") +BUILTIN(__builtin_xtensa_mula_ad_lh, "vUiIUi", "n") +BUILTIN(__builtin_xtensa_mula_ad_hl, "vUiIUi", "n") +BUILTIN(__builtin_xtensa_mula_ad_hh, "vUiIUi", "n") + +BUILTIN(__builtin_xtensa_mula_da_ll, "vIUiUi", "n") +BUILTIN(__builtin_xtensa_mula_da_lh, "vIUiUi", "n") +BUILTIN(__builtin_xtensa_mula_da_hl, "vIUiUi", "n") +BUILTIN(__builtin_xtensa_mula_da_hh, "vIUiUi", "n") + +BUILTIN(__builtin_xtensa_mula_dd_ll, "vIUiIUi", "n") +BUILTIN(__builtin_xtensa_mula_dd_lh, "vIUiIUi", "n") +BUILTIN(__builtin_xtensa_mula_dd_hl, "vIUiIUi", "n") +BUILTIN(__builtin_xtensa_mula_dd_hh, "vIUiIUi", "n") + +BUILTIN(__builtin_xtensa_muls_aa_ll, "vUiUi", "n") +BUILTIN(__builtin_xtensa_muls_aa_lh, "vUiUi", "n") +BUILTIN(__builtin_xtensa_muls_aa_hl, "vUiUi", "n") +BUILTIN(__builtin_xtensa_muls_aa_hh, "vUiUi", "n") + +BUILTIN(__builtin_xtensa_muls_ad_ll, "vUiIUi", "n") +BUILTIN(__builtin_xtensa_muls_ad_lh, "vUiIUi", "n") +BUILTIN(__builtin_xtensa_muls_ad_hl, "vUiIUi", "n") +BUILTIN(__builtin_xtensa_muls_ad_hh, "vUiIUi", "n") + +BUILTIN(__builtin_xtensa_muls_da_ll, "vIUiUi", "n") +BUILTIN(__builtin_xtensa_muls_da_lh, "vIUiUi", "n") +BUILTIN(__builtin_xtensa_muls_da_hl, "vIUiUi", "n") +BUILTIN(__builtin_xtensa_muls_da_hh, "vIUiUi", "n") + +BUILTIN(__builtin_xtensa_muls_dd_ll, "vIUiIUi", "n") +BUILTIN(__builtin_xtensa_muls_dd_lh, "vIUiIUi", "n") +BUILTIN(__builtin_xtensa_muls_dd_hl, "vIUiIUi", "n") +BUILTIN(__builtin_xtensa_muls_dd_hh, "vIUiIUi", "n") + +BUILTIN(__builtin_xtensa_mula_da_ll_lddec, "vIUii**IUii", "n") +BUILTIN(__builtin_xtensa_mula_da_lh_lddec, "vIUii**IUii", "n") +BUILTIN(__builtin_xtensa_mula_da_hl_lddec, "vIUii**IUii", "n") +BUILTIN(__builtin_xtensa_mula_da_hh_lddec, "vIUii**IUii", "n") + +BUILTIN(__builtin_xtensa_mula_da_ll_ldinc, "vIUii**IUii", "n") +BUILTIN(__builtin_xtensa_mula_da_lh_ldinc, "vIUii**IUii", "n") +BUILTIN(__builtin_xtensa_mula_da_hl_ldinc, "vIUii**IUii", "n") +BUILTIN(__builtin_xtensa_mula_da_hh_ldinc, "vIUii**IUii", "n") + +BUILTIN(__builtin_xtensa_mula_dd_ll_lddec, "vIUii**IUiIUi", "n") +BUILTIN(__builtin_xtensa_mula_dd_lh_lddec, "vIUii**IUiIUi", "n") +BUILTIN(__builtin_xtensa_mula_dd_hl_lddec, "vIUii**IUiIUi", "n") +BUILTIN(__builtin_xtensa_mula_dd_hh_lddec, "vIUii**IUiIUi", "n") + +BUILTIN(__builtin_xtensa_mula_dd_ll_ldinc, "vIUii**IUiIUi", "n") +BUILTIN(__builtin_xtensa_mula_dd_lh_ldinc, "vIUii**IUiIUi", "n") +BUILTIN(__builtin_xtensa_mula_dd_hl_ldinc, "vIUii**IUiIUi", "n") +BUILTIN(__builtin_xtensa_mula_dd_hh_ldinc, "vIUii**IUiIUi", "n") + +// Load operations + +BUILTIN(__builtin_xtensa_ldinc, "vIUii**", "n") +BUILTIN(__builtin_xtensa_lddec, "vIUii**", "n") + +// WSR/RSR/XSR + +BUILTIN(__builtin_xtensa_wsr_acclo, "vUi", "n") +BUILTIN(__builtin_xtensa_rsr_acclo, "Ui", "n") +BUILTIN(__builtin_xtensa_xsr_acclo, "vUi*", "n") +BUILTIN(__builtin_xtensa_wsr_acchi, "vUi", "n") +BUILTIN(__builtin_xtensa_rsr_acchi, "Ui", "n") +BUILTIN(__builtin_xtensa_xsr_acchi, "vUi*", "n") +BUILTIN(__builtin_xtensa_wsr_m0, "vUi", "n") +BUILTIN(__builtin_xtensa_rsr_m0, "Ui", "n") +BUILTIN(__builtin_xtensa_xsr_m0, "vUi*", "n") +BUILTIN(__builtin_xtensa_wsr_m1, "vUi", "n") +BUILTIN(__builtin_xtensa_rsr_m1, "Ui", "n") +BUILTIN(__builtin_xtensa_xsr_m1, "vUi*", "n") +BUILTIN(__builtin_xtensa_wsr_m2, "vUi", "n") +BUILTIN(__builtin_xtensa_rsr_m2, "Ui", "n") +BUILTIN(__builtin_xtensa_xsr_m2, "vUi*", "n") +BUILTIN(__builtin_xtensa_wsr_m3, "vUi", "n") +BUILTIN(__builtin_xtensa_rsr_m3, "Ui", "n") +BUILTIN(__builtin_xtensa_xsr_m3, "vUi*", "n") + +#undef BUILTIN \ No newline at end of file diff --git a/clang/include/clang/Basic/TargetBuiltins.h b/clang/include/clang/Basic/TargetBuiltins.h index d8ad9858d8c80..c26637e36bac3 100644 --- a/clang/include/clang/Basic/TargetBuiltins.h +++ b/clang/include/clang/Basic/TargetBuiltins.h @@ -336,6 +336,16 @@ namespace clang { }; } + /// Xtensa builtins + namespace Xtensa { + enum { + LastTIBuiltin = clang::Builtin::FirstTSBuiltin - 1, +#define BUILTIN(ID, TYPE, ATTRS) BI##ID, +#include "clang/Basic/BuiltinsXtensa.def" + LastTSBuiltin + }; + } // namespace Xtensa + static constexpr uint64_t LargestBuiltinID = std::max( {ARM::LastTSBuiltin, AArch64::LastTSBuiltin, BPF::LastTSBuiltin, PPC::LastTSBuiltin, NVPTX::LastTSBuiltin, AMDGPU::LastTSBuiltin, diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index b4f3a69259fad..426e43dec1150 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -313,7 +313,10 @@ class TargetInfo : public virtual TransferrableTargetInfo, // void *__saved_reg_area_end_pointer; // void *__overflow_area_pointer; //} va_list; - HexagonBuiltinVaList + HexagonBuiltinVaList, + + // Tensilica Xtensa + XtensaABIBuiltinVaList }; protected: diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 3cab37b21aaf3..fba9db466fe94 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -184,6 +184,8 @@ def m_x86_Features_Group : OptionGroup<"">, Group, Flags<[CoreOption]>, DocName<"X86">; def m_riscv_Features_Group : OptionGroup<"">, Group, DocName<"RISCV">; +def m_xtensa_Features_Group : OptionGroup<"">, + Group, Flags<[CoreOption]>, DocName<"Xtensa">; def m_libc_Group : OptionGroup<"">, Group, Flags<[HelpHidden]>; @@ -4662,6 +4664,15 @@ def mno_retpoline_external_thunk : Flag<["-"], "mno-retpoline-external-thunk">, def mvzeroupper : Flag<["-"], "mvzeroupper">, Group; def mno_vzeroupper : Flag<["-"], "mno-vzeroupper">, Group; +// Xtensa feature flags +def malways_memw : Flag<["-"], "malways-memw">, Group; +def mfix_esp32_psram_cache_issue : Flag<["-"], "mfix-esp32-psram-cache-issue">, Group; +def mfix_esp32_psram_cache_strategy_EQ : Joined<["-"], "mfix-esp32-psram-cache-strategy=">, Group, + HelpText<" Psram cache fix strategies : memw, nops">, + Values<"memw, nops">; +def mlongcalls : Flag<["-"], "mlongcalls">, Group; +def mtext_section_literals : Flag<["-"], "mtext-section-literals">, Group; + // These are legacy user-facing driver-level option spellings. They are always // aliases for options that are spelled using the more common Unix / GNU flag // style of double-dash and equals-joined flags. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 681a76dfa56a6..cd8ffbfabf21b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13117,6 +13117,7 @@ class Sema final { bool CheckRISCVLMUL(CallExpr *TheCall, unsigned ArgNum); bool CheckRISCVBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall); + bool CheckXtensaBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool SemaBuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall); bool SemaBuiltinVAStartARMMicrosoft(CallExpr *Call); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index cfd7bf6045422..07631732eb5ca 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -8968,6 +8968,51 @@ static TypedefDecl *CreateHexagonBuiltinVaListDecl(const ASTContext *Context) { return Context->buildImplicitTypedef(VaListTagArrayType, "__builtin_va_list"); } +static TypedefDecl * +CreateXtensaABIBuiltinVaListDecl(const ASTContext *Context) { + // typedef struct __va_list_tag { + RecordDecl *VaListTagDecl; + + VaListTagDecl = Context->buildImplicitRecord("__va_list_tag"); + VaListTagDecl->startDefinition(); + + const size_t NumFields = 3; + QualType FieldTypes[NumFields]; + const char *FieldNames[NumFields]; + + // int* __va_stk; + FieldTypes[0] = Context->getPointerType(Context->IntTy); + FieldNames[0] = "__va_stk"; + + // int* __va_reg; + FieldTypes[1] = Context->getPointerType(Context->IntTy); + FieldNames[1] = "__va_reg"; + + // int __va_ndx; + FieldTypes[2] = Context->IntTy; + FieldNames[2] = "__va_ndx"; + + // Create fields + for (unsigned i = 0; i < NumFields; ++i) { + FieldDecl *Field = FieldDecl::Create( + *Context, VaListTagDecl, SourceLocation(), SourceLocation(), + &Context->Idents.get(FieldNames[i]), FieldTypes[i], /*TInfo=*/nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/false, ICIS_NoInit); + Field->setAccess(AS_public); + VaListTagDecl->addDecl(Field); + } + VaListTagDecl->completeDefinition(); + Context->VaListTagDecl = VaListTagDecl; + QualType VaListTagType = Context->getRecordType(VaListTagDecl); + + // } __va_list_tag; + TypedefDecl *VaListTagTypedefDecl = + Context->buildImplicitTypedef(VaListTagType, "__builtin_va_list"); + + return VaListTagTypedefDecl; +} + static TypedefDecl *CreateVaListDecl(const ASTContext *Context, TargetInfo::BuiltinVaListKind Kind) { switch (Kind) { @@ -8989,6 +9034,8 @@ static TypedefDecl *CreateVaListDecl(const ASTContext *Context, return CreateSystemZBuiltinVaListDecl(Context); case TargetInfo::HexagonBuiltinVaList: return CreateHexagonBuiltinVaListDecl(Context); + case TargetInfo::XtensaABIBuiltinVaList: + return CreateXtensaABIBuiltinVaListDecl(Context); } llvm_unreachable("Unhandled __builtin_va_list type kind"); diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt index 3e052c0cf9957..ca9dfea45b0f4 100644 --- a/clang/lib/Basic/CMakeLists.txt +++ b/clang/lib/Basic/CMakeLists.txt @@ -98,6 +98,7 @@ add_clang_library(clangBasic Targets/WebAssembly.cpp Targets/X86.cpp Targets/XCore.cpp + Targets/Xtensa.cpp TokenKinds.cpp TypeTraits.cpp Version.cpp diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp index 2d6ef998485ae..8d2d52f11e296 100644 --- a/clang/lib/Basic/Targets.cpp +++ b/clang/lib/Basic/Targets.cpp @@ -40,6 +40,7 @@ #include "Targets/WebAssembly.h" #include "Targets/X86.h" #include "Targets/XCore.h" +#include "Targets/Xtensa.h" #include "clang/Basic/Diagnostic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" @@ -670,6 +671,9 @@ TargetInfo *AllocateTarget(const llvm::Triple &Triple, default: return new CSKYTargetInfo(Triple, Opts); } + + case llvm::Triple::xtensa : + return new XtensaTargetInfo(Triple, Opts); } } } // namespace targets diff --git a/clang/lib/Basic/Targets/Xtensa.cpp b/clang/lib/Basic/Targets/Xtensa.cpp new file mode 100644 index 0000000000000..feeedd26f62ed --- /dev/null +++ b/clang/lib/Basic/Targets/Xtensa.cpp @@ -0,0 +1,92 @@ +//===--- Xtensa.cpp - Implement Xtensa target feature support -------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements Xtensa TargetInfo objects. +// +//===----------------------------------------------------------------------===// + +#include "Xtensa.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/MacroBuilder.h" +#include "clang/Basic/TargetBuiltins.h" + +using namespace clang; +using namespace clang::targets; + +const Builtin::Info XtensaTargetInfo::BuiltinInfo[] = { +#define BUILTIN(ID, TYPE, ATTRS) \ + {#ID, TYPE, ATTRS, nullptr, ALL_LANGUAGES, nullptr}, +#include "clang/Basic/BuiltinsXtensa.def" +}; + +void XtensaTargetInfo::getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const { + Builder.defineMacro("__ELF__"); + Builder.defineMacro("__xtensa__"); + Builder.defineMacro("__XTENSA__"); + Builder.defineMacro("__XTENSA_EL__"); + if (HasWindowed) + Builder.defineMacro("__XTENSA_WINDOWED_ABI__"); + else + Builder.defineMacro("__XTENSA_CALL0_ABI__"); + if (!HasFP) + Builder.defineMacro("__XTENSA_SOFT_FLOAT__"); +} + +ArrayRef XtensaTargetInfo::getTargetBuiltins() const { + return llvm::makeArrayRef(BuiltinInfo, clang::Xtensa::LastTSBuiltin - + Builtin::FirstTSBuiltin); +} + +void XtensaTargetInfo::fillValidCPUList( + SmallVectorImpl &Values) const { + llvm::Xtensa::fillValidCPUList(Values); +} + +bool XtensaTargetInfo::initFeatureMap( + llvm::StringMap &Features, DiagnosticsEngine &Diags, StringRef CPU, + const std::vector &FeaturesVec) const { + + // Assume that by default cpu is esp32 + if (CPU.empty()) + CPU = "esp32"; + + CPU = llvm::Xtensa::getBaseName(CPU); + + SmallVector CPUFeatures; + llvm::Xtensa::getCPUFeatures(CPU, CPUFeatures); + + for (auto Feature : CPUFeatures) + if (Feature[0] == '+') + Features[Feature.drop_front(1)] = true; + + return TargetInfo::initFeatureMap(Features, Diags, CPU, FeaturesVec); +} + +/// Return true if has this feature, need to sync with handleTargetFeatures. +bool XtensaTargetInfo::hasFeature(StringRef Feature) const { + return llvm::StringSwitch(Feature) + .Case("fp", HasFP) + .Case("windowed", HasWindowed) + .Default(false); +} + +/// Perform initialization based on the user configured set of features. +bool XtensaTargetInfo::handleTargetFeatures(std::vector &Features, + DiagnosticsEngine &Diags) { + for (const auto &Feature : Features) { + if (Feature == "+fp") + HasFP = true; + else if (Feature == "+windowed") + HasWindowed = true; + } + + return true; +} diff --git a/clang/lib/Basic/Targets/Xtensa.h b/clang/lib/Basic/Targets/Xtensa.h new file mode 100644 index 0000000000000..b243a91c679e4 --- /dev/null +++ b/clang/lib/Basic/Targets/Xtensa.h @@ -0,0 +1,122 @@ +//===--- Xtensa.h - Declare Xtensa target feature support -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares Xtensa TargetInfo objects. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_BASIC_TARGETS_XTENSA_H +#define LLVM_CLANG_LIB_BASIC_TARGETS_XTENSA_H + +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/TargetParser.h" + +#include "clang/Basic/Builtins.h" +#include "clang/Basic/MacroBuilder.h" +#include "clang/Basic/TargetBuiltins.h" + +namespace clang { +namespace targets { + +class LLVM_LIBRARY_VISIBILITY XtensaTargetInfo : public TargetInfo { + static const Builtin::Info BuiltinInfo[]; + std::string CPU; + bool HasFP = false; + bool HasWindowed = false; + +public: + XtensaTargetInfo(const llvm::Triple &Triple, const TargetOptions &) + : TargetInfo(Triple) { + BigEndian = false; + NoAsmVariants = true; + LongLongAlign = 64; + SuitableAlign = 32; + DoubleAlign = LongDoubleAlign = 64; + SizeType = UnsignedInt; + PtrDiffType = SignedInt; + IntPtrType = SignedInt; + WCharType = UnsignedChar; + WIntType = UnsignedInt; + UseZeroLengthBitfieldAlignment = true; + MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 32; + resetDataLayout("e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32"); + } + + void getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const override; + + ArrayRef getTargetBuiltins() const override; + + BuiltinVaListKind getBuiltinVaListKind() const override { + + return TargetInfo::XtensaABIBuiltinVaList; + } + + const char *getClobbers() const override { return ""; } + + ArrayRef getGCCRegNames() const override { + static const char *const GCCRegNames[] = { + // General register name + "a0", "sp", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10", + "a11", "a12", "a13", "a14", "a15", + // Special register name + "sar"}; + return llvm::makeArrayRef(GCCRegNames); + } + + ArrayRef getGCCRegAliases() const override { + return None; + } + + bool validateAsmConstraint(const char *&Name, + TargetInfo::ConstraintInfo &Info) const override { + switch (*Name) { + default: + return false; + case 'a': + case 'f': + Info.setAllowsRegister(); + return true; + } + return false; + } + + int getEHDataRegisterNumber(unsigned RegNo) const override { + return (RegNo < 2) ? RegNo : -1; + } + + bool isValidCPUName(StringRef Name) const override { + return llvm::Xtensa::parseCPUKind(Name) != llvm::Xtensa::CK_INVALID; + } + + bool setCPU(const std::string &Name) override { + CPU = Name; + return isValidCPUName(Name); + } + + void fillValidCPUList(SmallVectorImpl &Values) const override; + + bool + initFeatureMap(llvm::StringMap &Features, DiagnosticsEngine &Diags, + StringRef CPU, + const std::vector &FeaturesVec) const override; + + bool hasFeature(StringRef Feature) const override; + + bool handleTargetFeatures(std::vector &Features, + DiagnosticsEngine &Diags) override; +}; +} // namespace targets +} // namespace clang +#endif // LLVM_CLANG_LIB_BASIC_TARGETS_XTENSA_H diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 36e10e4df4c19..2d714df600fb8 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -11535,6 +11535,234 @@ class CSKYTargetCodeGenInfo : public TargetCodeGenInfo { }; } // end anonymous namespace +//===----------------------------------------------------------------------===// +// Xtensa ABI Implementation +//===----------------------------------------------------------------------===// + +namespace { +class XtensaABIInfo : public DefaultABIInfo { +private: + static const int MaxNumArgGPRs = 6; + static const int MaxNumRetGPRs = 4; + +public: + XtensaABIInfo(CodeGen::CodeGenTypes &CGT) : DefaultABIInfo(CGT) {} + + // DefaultABIInfo's classifyReturnType and classifyArgumentType are + // non-virtual, but computeInfo is virtual, so we overload it. + void computeInfo(CGFunctionInfo &FI) const override; + + ABIArgInfo classifyArgumentType(QualType Ty, int &ArgGPRsLeft) const; + + ABIArgInfo classifyReturnType(QualType RetTy) const; + + Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, + QualType Ty) const override; + + ABIArgInfo extendType(QualType Ty) const; +}; +} // end anonymous namespace + +void XtensaABIInfo::computeInfo(CGFunctionInfo &FI) const { + QualType RetTy = FI.getReturnType(); + if (!getCXXABI().classifyReturnType(FI)) + FI.getReturnInfo() = classifyReturnType(RetTy); + + int ArgGPRsLeft = MaxNumArgGPRs; + for (auto &ArgInfo : FI.arguments()) { + ArgInfo.info = classifyArgumentType(ArgInfo.type, ArgGPRsLeft); + } +} + +ABIArgInfo XtensaABIInfo::classifyArgumentType(QualType Ty, + int &ArgGPRsLeft) const { + assert(ArgGPRsLeft <= MaxNumArgGPRs && "Arg GPR tracking underflow"); + Ty = useFirstFieldIfTransparentUnion(Ty); + // Structures with either a non-trivial destructor or a non-trivial + // copy constructor are always passed indirectly. + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) { + if (ArgGPRsLeft) + ArgGPRsLeft -= 1; + return getNaturalAlignIndirect(Ty, /*ByVal=*/RAA == + CGCXXABI::RAA_DirectInMemory); + } + + // Ignore empty structs/unions. + if (isEmptyRecord(getContext(), Ty, true)) + return ABIArgInfo::getIgnore(); + + uint64_t Size = getContext().getTypeSize(Ty); + uint64_t NeededAlign = getContext().getTypeAlign(Ty); + bool MustUseStack = false; + int NeededArgGPRs = (Size + 31) / 32; + + if (NeededAlign == (2 * 32)) + NeededArgGPRs += (ArgGPRsLeft % 2); + + // Put on stack objects which are not fit to 6 registers, + // also on stack object which alignment more then 16 bytes and + // object with 16-byte alignment if it isn't the first argument. + if ((NeededArgGPRs > ArgGPRsLeft) || (NeededAlign > (4 * 32)) || + ((ArgGPRsLeft < 6) && (NeededAlign == (4 * 32)))) { + MustUseStack = true; + NeededArgGPRs = ArgGPRsLeft; + } + ArgGPRsLeft -= NeededArgGPRs; + + if (!isAggregateTypeForABI(Ty) && !Ty->isVectorType() && !MustUseStack) { + // Treat an enum type as its underlying type. + if (const EnumType *EnumTy = Ty->getAs()) + Ty = EnumTy->getDecl()->getIntegerType(); + // All integral types are promoted to XLen width, unless passed on the + // stack. + if (Size < 32 && Ty->isIntegralOrEnumerationType() && !MustUseStack) { + return extendType(Ty); + } + // Assume that type has 32, 64 or 128 bits + return ABIArgInfo::getDirect(llvm::IntegerType::get(getVMContext(), Size)); + } + + // Aggregates which are <= 6*32 will be passed in registers if possible, + // so coerce to integers. + if ((Size <= (MaxNumArgGPRs * 32)) && (!MustUseStack)) { + if (Size <= 32) { + return ABIArgInfo::getDirect(llvm::IntegerType::get(getVMContext(), 32)); + } else if (NeededAlign == (2 * 32)) { + return ABIArgInfo::getDirect(llvm::ArrayType::get( + llvm::IntegerType::get(getVMContext(), 64), NeededArgGPRs / 2)); + } else if (NeededAlign == (4 * 32)) { + return ABIArgInfo::getDirect(llvm::IntegerType::get(getVMContext(), 128)); + } else { + return ABIArgInfo::getDirect(llvm::ArrayType::get( + llvm::IntegerType::get(getVMContext(), 32), NeededArgGPRs)); + } + } +#undef MAX_STRUCT_IN_REGS_SIZE + return getNaturalAlignIndirect(Ty, /*ByVal=*/true); +} + +ABIArgInfo XtensaABIInfo::classifyReturnType(QualType RetTy) const { + if (RetTy->isVoidType()) + return ABIArgInfo::getIgnore(); + + int ArgGPRsLeft = MaxNumRetGPRs; + auto RetSize = llvm::alignTo(getContext().getTypeSize(RetTy), 32) / 32; + + // The rules for return and argument with type size more then 4 bytes + // are the same, so defer to classifyArgumentType. + if (RetSize > 1) + return classifyArgumentType(RetTy, ArgGPRsLeft); + + return DefaultABIInfo::classifyReturnType(RetTy); +} + +Address XtensaABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, + QualType Ty) const { + // The va_list structure memory layout: + // struct __va_list_tag { + // int32_t *va_stk; + // int32_t *va_reg; + // int32_t va_ndx; + // }; + CGBuilderTy &Builder = CGF.Builder; + + Address OverflowAreaPtr = Builder.CreateStructGEP(VAListAddr, 0, "__va_stk"); + Address OverflowArea = Address(Builder.CreateLoad(OverflowAreaPtr, ""), + CGF.Int32Ty, CharUnits::fromQuantity(4)); + Address RegSaveAreaPtr = Builder.CreateStructGEP(VAListAddr, 1, "__va_reg"); + Address RegSaveArea = Address(Builder.CreateLoad(RegSaveAreaPtr, ""), + CGF.Int32Ty, CharUnits::fromQuantity(4)); + Address ARAreaPtr = Builder.CreateStructGEP(VAListAddr, 2, "__va_ndx"); + llvm::Value *ARIndex = Builder.CreateLoad(ARAreaPtr, ""); + + ARIndex = Builder.CreateLShr(ARIndex, Builder.getInt32(2)); + + unsigned Align = getContext().getTypeAlign(Ty) / 32; + unsigned Size = (getContext().getTypeSize(Ty) + 31) / 32; + + if (Align > 1) { + ARIndex = Builder.CreateAdd(ARIndex, Builder.getInt32(Align - 1)); + ARIndex = + Builder.CreateAnd(ARIndex, Builder.getInt32((uint32_t) ~(Align - 1))); + } + + llvm::Value *ARIndexNext = Builder.CreateAdd(ARIndex, Builder.getInt32(Size)); + Builder.CreateStore(Builder.CreateShl(ARIndexNext, Builder.getInt32(2)), + ARAreaPtr); + + const unsigned OverflowLimit = 6; + llvm::Value *CC = Builder.CreateICmpULE( + ARIndexNext, Builder.getInt32(OverflowLimit), "cond"); + + llvm::BasicBlock *UsingRegSaveArea = + CGF.createBasicBlock("using_regsavearea"); + llvm::BasicBlock *UsingOverflow = CGF.createBasicBlock("using_overflow"); + llvm::BasicBlock *Cont = CGF.createBasicBlock("cont"); + + Builder.CreateCondBr(CC, UsingRegSaveArea, UsingOverflow); + + llvm::Type *DirectTy = CGF.ConvertType(Ty); + + // Case 1: consume registers. + Address RegAddr = Address::invalid(); + { + CGF.EmitBlock(UsingRegSaveArea); + + CharUnits RegSize = CharUnits::fromQuantity(4); + RegSaveArea = + Address(Builder.CreateInBoundsGEP(CGF.Int32Ty, RegSaveArea.getPointer(), + ARIndex), + CGF.Int32Ty, RegSaveArea.getAlignment().alignmentOfArrayElement(RegSize)); + RegAddr = Builder.CreateElementBitCast(RegSaveArea, DirectTy); + CGF.EmitBranch(Cont); + } + + // Case 2: consume space in the overflow area. + Address MemAddr = Address::invalid(); + { + CGF.EmitBlock(UsingOverflow); + llvm::Value *CC1 = Builder.CreateICmpULE( + ARIndex, Builder.getInt32(OverflowLimit), "cond_overflow"); + + llvm::Value *ARIndexOff = Builder.CreateSelect( + CC1, Builder.CreateSub(Builder.getInt32(8), ARIndex), + Builder.getInt32(0)); + + llvm::Value *ARIndexCorr = Builder.CreateAdd(ARIndex, ARIndexOff); + llvm::Value *ARIndexNextCorr = Builder.CreateAdd(ARIndexNext, ARIndexOff); + Builder.CreateStore(Builder.CreateShl(ARIndexNextCorr, Builder.getInt32(2)), + ARAreaPtr); + + CharUnits RegSize = CharUnits::fromQuantity(4); + OverflowArea = + Address(Builder.CreateInBoundsGEP( + CGF.Int32Ty, OverflowArea.getPointer(), ARIndexCorr), + CGF.Int32Ty, OverflowArea.getAlignment().alignmentOfArrayElement(RegSize)); + MemAddr = Builder.CreateElementBitCast(OverflowArea, DirectTy); + CGF.EmitBranch(Cont); + } + + CGF.EmitBlock(Cont); + + // Merge the cases with a phi. + Address Result = + emitMergePHI(CGF, RegAddr, UsingRegSaveArea, MemAddr, UsingOverflow, ""); + + return Result; +} + +ABIArgInfo XtensaABIInfo::extendType(QualType Ty) const { + return ABIArgInfo::getExtend(Ty); +} + +namespace { +class XtensaTargetCodeGenInfo : public TargetCodeGenInfo { +public: + XtensaTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) + : TargetCodeGenInfo(std::make_unique(CGT)) {} +}; +} // namespace + //===----------------------------------------------------------------------===// // Driver code //===----------------------------------------------------------------------===// @@ -11763,6 +11991,8 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { : hasFP64 ? 64 : 32)); } + case llvm::Triple::xtensa: + return SetCGInfo(new XtensaTargetCodeGenInfo(Types)); } } diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt index 18c9b2d042f6c..05263ad676ccf 100644 --- a/clang/lib/Driver/CMakeLists.txt +++ b/clang/lib/Driver/CMakeLists.txt @@ -80,6 +80,7 @@ add_clang_library(clangDriver ToolChains/VEToolchain.cpp ToolChains/WebAssembly.cpp ToolChains/XCore.cpp + ToolChains/Xtensa.cpp ToolChains/PPCLinux.cpp ToolChains/PPCFreeBSD.cpp ToolChains/InterfaceStubs.cpp diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 3f29afd359718..a019143b7dbff 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -51,6 +51,7 @@ #include "ToolChains/VEToolchain.h" #include "ToolChains/WebAssembly.h" #include "ToolChains/XCore.h" +#include "ToolChains/Xtensa.h" #include "ToolChains/ZOS.h" #include "clang/Basic/TargetID.h" #include "clang/Basic/Version.h" @@ -6115,6 +6116,9 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, case llvm::Triple::csky: TC = std::make_unique(*this, Target, Args); break; + case llvm::Triple::xtensa: + TC = std::make_unique(*this, Target, Args); + break; default: if (Target.getVendor() == llvm::Triple::Myriad) TC = std::make_unique(*this, Target, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 3704ed8586682..970c56c152cda 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -23,6 +23,7 @@ #include "Hexagon.h" #include "MSP430.h" #include "PS4CPU.h" +#include "Xtensa.h" #include "clang/Basic/CLWarnings.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/CodeGenOptions.h" @@ -371,6 +372,9 @@ static void getTargetFeatures(const Driver &D, const llvm::Triple &Triple, case llvm::Triple::csky: csky::getCSKYTargetFeatures(D, Triple, Args, CmdArgs, Features); break; + case llvm::Triple::xtensa: + xtensa::getXtensaTargetFeatures(D, Args, Features); + break; } for (auto Feature : unifyTargetFeatures(Features)) { @@ -536,6 +540,7 @@ static bool useFramePointerForTargetByDefault(const ArgList &Args, case llvm::Triple::amdgcn: case llvm::Triple::r600: case llvm::Triple::csky: + case llvm::Triple::xtensa: return !areOptimizationsEnabled(Args); default: break; @@ -1844,6 +1849,10 @@ void Clang::RenderTargetOptions(const llvm::Triple &EffectiveTriple, case llvm::Triple::ve: AddVETargetArgs(Args, CmdArgs); break; + + case llvm::Triple::xtensa: + AddXtensaTargetArgs(Args, CmdArgs); + break; } } @@ -2394,6 +2403,39 @@ void Clang::AddVETargetArgs(const ArgList &Args, ArgStringList &CmdArgs) const { CmdArgs.push_back("hard"); } +void Clang::AddXtensaTargetArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + const Driver &D = getToolChain().getDriver(); + + if (Args.getLastArg(options::OPT_malways_memw) != nullptr) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-malways-memw"); + } + + if (Args.getLastArg(options::OPT_mfix_esp32_psram_cache_issue) != nullptr) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-mfix-esp32-psram-cache-issue"); + + if (Arg *A = + Args.getLastArg(options::OPT_mfix_esp32_psram_cache_strategy_EQ)) { + StringRef Value = A->getValue(); + if (Value == "memw" || Value == "nops") { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back( + Args.MakeArgString("-mfix-esp32-psram-cache-strategy=" + Value)); + } else { + D.Diag(diag::err_drv_unsupported_option_argument) + << A->getOption().getName() << Value; + } + } + } + + if (Args.getLastArg(options::OPT_mtext_section_literals) != nullptr) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-mtext-section-literals"); + } +} + void Clang::DumpCompilationDatabase(Compilation &C, StringRef Filename, StringRef Target, const InputInfo &Output, const InputInfo &Input, const ArgList &Args) const { diff --git a/clang/lib/Driver/ToolChains/Clang.h b/clang/lib/Driver/ToolChains/Clang.h index 5209c6687599b..4d73bed0c6c75 100644 --- a/clang/lib/Driver/ToolChains/Clang.h +++ b/clang/lib/Driver/ToolChains/Clang.h @@ -79,6 +79,8 @@ class LLVM_LIBRARY_VISIBILITY Clang : public Tool { llvm::opt::ArgStringList &CmdArgs) const; void AddVETargetArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const; + void AddXtensaTargetArgs(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs) const; enum RewriteKind { RK_None, RK_Fragile, RK_NonFragile }; diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index 443725f7d8a8d..48d5437cbac38 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -462,6 +462,11 @@ std::string tools::getCPUName(const Driver &D, const ArgList &Args, case llvm::Triple::wasm32: case llvm::Triple::wasm64: return std::string(getWebAssemblyTargetCPU(Args)); + + case llvm::Triple::xtensa: + if (const Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) + return A->getValue(); + return ""; } } diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp index f203cae1d329f..7608b87cda31a 100644 --- a/clang/lib/Driver/ToolChains/Gnu.cpp +++ b/clang/lib/Driver/ToolChains/Gnu.cpp @@ -1039,6 +1039,10 @@ static Multilib makeMultilib(StringRef commonSuffix) { return Multilib(commonSuffix, commonSuffix, commonSuffix); } +static Multilib makeMultilib(StringRef commonSuffix, int Priority) { + return Multilib(commonSuffix, commonSuffix, commonSuffix, Priority); +} + static bool findMipsCsMultilibs(const Multilib::flags_list &Flags, FilterNonExistent &NonExistent, DetectedMultilibs &Result) { @@ -1683,28 +1687,59 @@ static void findRISCVBareMetalMultilibs(const Driver &D, // TODO: support MULTILIB_REUSE constexpr RiscvMultilib RISCVMultilibSet[] = { {"rv32i", "ilp32"}, {"rv32im", "ilp32"}, {"rv32iac", "ilp32"}, + {"rv32imc", "ilp32"}, {"rv32imac", "ilp32"}, {"rv32imafc", "ilp32f"}, {"rv64imac", "lp64"}, {"rv64imafdc", "lp64d"}}; std::vector Ms; + if (TargetTriple.getVendor() == llvm::Triple::Espressif) { + Ms.emplace_back(Multilib()); + Ms.emplace_back(makeMultilib("no-rtti", 1) + .flag("+fno-rtti") + .flag("-frtti")); + } for (auto Element : RISCVMultilibSet) { - // multilib path rule is ${march}/${mabi} - Ms.emplace_back( - makeMultilib((Twine(Element.march) + "/" + Twine(Element.mabi)).str()) - .flag(Twine("+march=", Element.march).str()) - .flag(Twine("+mabi=", Element.mabi).str())); + if (TargetTriple.getVendor() == llvm::Triple::Espressif) { + // multilib path rule is ${march}/${mabi} + Ms.emplace_back( + makeMultilib((Twine(Element.march) + "/" + Twine(Element.mabi)).str(), 2) + .flag(Twine("+march=", Element.march).str()) + .flag(Twine("+mabi=", Element.mabi).str())); + /* no-rtti version for every ${march}/${mabi} */ + Ms.emplace_back( + makeMultilib((Twine(Element.march) + "/" + Twine(Element.mabi) + "/no-rtti").str(), 3) + .flag(Twine("+march=", Element.march).str()) + .flag(Twine("+mabi=", Element.mabi).str()) + .flag("+fno-rtti") + .flag("-frtti")); + } else { + // multilib path rule is ${march}/${mabi} + Ms.emplace_back( + makeMultilib((Twine(Element.march) + "/" + Twine(Element.mabi)).str()) + .flag(Twine("+march=", Element.march).str()) + .flag(Twine("+mabi=", Element.mabi).str())); + } } MultilibSet RISCVMultilibs = MultilibSet() .Either(ArrayRef(Ms)) - .FilterOut(NonExistent) + .FilterOut(NonExistent); + if (TargetTriple.getVendor() == llvm::Triple::Espressif) { + RISCVMultilibs + .setFilePathsCallback([](const Multilib &M) { + return std::vector( + {M.gccSuffix(), + "/../../../../riscv32-esp-elf/lib" + M.gccSuffix()}); + }); + } else { + RISCVMultilibs .setFilePathsCallback([](const Multilib &M) { return std::vector( {M.gccSuffix(), "/../../../../riscv64-unknown-elf/lib" + M.gccSuffix(), "/../../../../riscv32-unknown-elf/lib" + M.gccSuffix()}); }); - + } Multilib::flags_list Flags; llvm::StringSet<> Added_ABIs; @@ -1720,6 +1755,12 @@ static void findRISCVBareMetalMultilibs(const Driver &D, } } + if (TargetTriple.getVendor() == llvm::Triple::Espressif) { + addMultilibFlag( + Args.hasFlag(options::OPT_frtti, options::OPT_fno_rtti, true), "frtti", + Flags); + } + if (RISCVMultilibs.select(Flags, Result.SelectedMultilib)) Result.Multilibs = RISCVMultilibs; } @@ -1761,6 +1802,42 @@ static void findRISCVMultilibs(const Driver &D, Result.Multilibs = RISCVMultilibs; } +static void findXtensaMultilibs(const Driver &D, + const llvm::Triple &TargetTriple, StringRef Path, + const ArgList &Args, DetectedMultilibs &Result) { + + MultilibSet XtensaMultilibs = MultilibSet(); + bool IsESP32 = Args.getLastArgValue(options::OPT_mcpu_EQ, "esp32").equals("esp32"); + + XtensaMultilibs.push_back(Multilib()); + if (IsESP32) + XtensaMultilibs.push_back(Multilib("esp32-psram", {}, {}, 2) + .flag("+mfix-esp32-psram-cache-issue")); + + XtensaMultilibs.push_back( + Multilib("no-rtti", {}, {}, 1).flag("+fno-rtti").flag("-frtti")); + + if (IsESP32) + XtensaMultilibs.push_back(Multilib("esp32-psram/no-rtti", {}, {}, 3) + .flag("+fno-rtti") + .flag("-frtti") + .flag("+mfix-esp32-psram-cache-issue")); + + Multilib::flags_list Flags; + addMultilibFlag( + Args.hasFlag(options::OPT_frtti, options::OPT_fno_rtti, false), "frtti", + Flags); + + if (IsESP32) + addMultilibFlag(Args.hasFlag(options::OPT_mfix_esp32_psram_cache_issue, + options::OPT_mfix_esp32_psram_cache_issue, + false), + "mfix-esp32-psram-cache-issue", Flags); + + if (XtensaMultilibs.select(Flags, Result.SelectedMultilib)) + Result.Multilibs = XtensaMultilibs; +} + static bool findBiarchMultilibs(const Driver &D, const llvm::Triple &TargetTriple, StringRef Path, const ArgList &Args, @@ -2286,7 +2363,9 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes( static const char *const RISCV32LibDirs[] = {"/lib32", "/lib"}; static const char *const RISCV32Triples[] = {"riscv32-unknown-linux-gnu", "riscv32-linux-gnu", - "riscv32-unknown-elf"}; + "riscv32-unknown-elf", + "riscv32-esp-elf", + "riscv32-esp-unknown-elf"}; static const char *const RISCV64LibDirs[] = {"/lib64", "/lib"}; static const char *const RISCV64Triples[] = {"riscv64-unknown-linux-gnu", "riscv64-linux-gnu", @@ -2304,6 +2383,9 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes( "s390x-linux-gnu", "s390x-unknown-linux-gnu", "s390x-ibm-linux-gnu", "s390x-suse-linux", "s390x-redhat-linux"}; + static const char *const XtensaLibDirs[] = {"/lib"}; + static const char *const XtensaTriples[] = { + "xtensa-esp-elf", "xtensa-esp-unknown-elf"}; using std::begin; using std::end; @@ -2571,6 +2653,10 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes( LibDirs.append(begin(SystemZLibDirs), end(SystemZLibDirs)); TripleAliases.append(begin(SystemZTriples), end(SystemZTriples)); break; + case llvm::Triple::xtensa: + LibDirs.append(begin(XtensaLibDirs), end(XtensaLibDirs)); + TripleAliases.append(begin(XtensaTriples), end(XtensaTriples)); + break; default: // By default, just rely on the standard lib directories and the original // triple. @@ -2609,6 +2695,8 @@ bool Generic_GCC::GCCInstallationDetector::ScanGCCForMultilibs( findMSP430Multilibs(D, TargetTriple, Path, Args, Detected); } else if (TargetArch == llvm::Triple::avr) { // AVR has no multilibs. + } else if (TargetArch == llvm::Triple::xtensa) { + findXtensaMultilibs(D, TargetTriple, Path, Args, Detected); } else if (!findBiarchMultilibs(D, TargetTriple, Path, Args, NeedsBiarchSuffix, Detected)) { return false; diff --git a/clang/lib/Driver/ToolChains/RISCVToolchain.cpp b/clang/lib/Driver/ToolChains/RISCVToolchain.cpp index 3491de22d3719..f71543ac7d303 100644 --- a/clang/lib/Driver/ToolChains/RISCVToolchain.cpp +++ b/clang/lib/Driver/ToolChains/RISCVToolchain.cpp @@ -184,7 +184,17 @@ void RISCV::Linker::ConstructJob(Compilation &C, const JobAction &JA, } if (WantCRTs) { - CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crt0.o"))); + /* Espressif toolcahin uses newlib. crt0.o from it refers to 'main' symbol. + In 'freestanding' mode 'main' is not marked as special symbol by clang, + so when compiling C++ program with 'clang++' 'main' gets mmangled + (if not decalred as 'extern "C"' ) and linker can not resolve it. + The problem can happen, for example, when cmake checks C++ compiler by buiding simple C++ code, + unfortunately 'main' function in that code is not decalred as 'extern "C"'. */ + bool Freestanding = + Args.hasFlag(options::OPT_ffreestanding, options::OPT_fhosted, false); + if (!Freestanding || ToolChain.getTriple().getVendor() != llvm::Triple::Espressif) { + CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crt0.o"))); + } CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crtbegin))); } @@ -209,6 +219,9 @@ void RISCV::Linker::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("--start-group"); CmdArgs.push_back("-lc"); CmdArgs.push_back("-lgloss"); + if (ToolChain.getTriple().getVendor() == llvm::Triple::Espressif) { + CmdArgs.push_back("-lnosys"); + } CmdArgs.push_back("--end-group"); AddRunTimeLibs(ToolChain, ToolChain.getDriver(), CmdArgs, Args); } diff --git a/clang/lib/Driver/ToolChains/RISCVToolchain.h b/clang/lib/Driver/ToolChains/RISCVToolchain.h index 46b94bdb54e09..dd4437f0a465d 100644 --- a/clang/lib/Driver/ToolChains/RISCVToolchain.h +++ b/clang/lib/Driver/ToolChains/RISCVToolchain.h @@ -35,6 +35,12 @@ class LLVM_LIBRARY_VISIBILITY RISCVToolChain : public Generic_ELF { addLibStdCxxIncludePaths(const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args) const override; + bool IsIntegratedAssemblerDefault() const override { + if (GCCInstallation.getTriple().getVendor() == llvm::Triple::Espressif) + return false; + return Generic_ELF::IsIntegratedAssemblerDefault(); + } + protected: Tool *buildLinker() const override; diff --git a/clang/lib/Driver/ToolChains/Xtensa.cpp b/clang/lib/Driver/ToolChains/Xtensa.cpp new file mode 100644 index 0000000000000..fb5611749cdfe --- /dev/null +++ b/clang/lib/Driver/ToolChains/Xtensa.cpp @@ -0,0 +1,370 @@ +//===--- Xtensa.cpp - Xtensa ToolChain Implementations ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Xtensa.h" +#include "CommonArgs.h" +#include "clang/Basic/Cuda.h" +#include "clang/Config/config.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Distro.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TargetParser.h" +#include "llvm/Support/VirtualFileSystem.h" +#include + +using namespace clang::driver; +using namespace clang::driver::tools; +using namespace clang::driver::toolchains; +using namespace clang; +using namespace llvm::opt; + +using tools::addMultilibFlag; + +/// Xtensa Toolchain +XtensaToolChain::XtensaToolChain(const Driver &D, const llvm::Triple &Triple, + const ArgList &Args) + : Generic_ELF(D, Triple, Args) { + + std::vector ExtraAliases; + + if (Triple.getVendor() == llvm::Triple::Espressif || + Triple.getVendor() == llvm::Triple::UnknownVendor) { + Arg *mcpuArg = Args.getLastArg(options::OPT_mcpu_EQ); + SmallString<128> CpuName; + if (mcpuArg) + CpuName = mcpuArg->getValue(); + else if (Triple.getVendor() == llvm::Triple::Espressif) + // 'esp32' is default for 'xtensa-esp-xxx' targets, + // for generic 'xtensa' target CPU should be always specified explicitly with '-mcpu' + CpuName = "esp32"; + if (CpuName.startswith("esp")) { + // ESP Xtensa GCC toolchain uses shorten triple "xtensa--elf", so add it as an alias + // to help Clang detect GCC installation properly + ExtraAliases = {std::string("xtensa-") + CpuName.c_str() + "-elf"}; + if (Args.hasArg(options::OPT_v)) { + llvm::errs() << "Use GCC target extra alias: " << ExtraAliases[0] << "\n"; + } + } + } + + GCCInstallation.init(Triple, Args, ExtraAliases); + if (GCCInstallation.isValid()) { + for (auto *A : Args) { + std::string Str = A->getAsString(Args); + if (!Str.compare("-mlongcalls")) + A->claim(); + if (!Str.compare("-fno-tree-switch-conversion")) + A->claim(); + + // Currently don't use integrated assembler for assembler input files + if ((IsIntegratedAsm) && (Str.length() > 2)) { + std::string ExtSubStr = Str.substr(Str.length() - 2); + if (!ExtSubStr.compare(".s")) + IsIntegratedAsm = false; + if (!ExtSubStr.compare(".S")) + IsIntegratedAsm = false; + } + } + + // Currently don't use integrated assembler for assembler input files + if (IsIntegratedAsm) { + if (Args.getLastArgValue(options::OPT_x).equals("assembler")) + IsIntegratedAsm = false; + + if (Args.getLastArgValue(options::OPT_x).equals("assembler-with-cpp")) + IsIntegratedAsm = false; + } + + Multilibs = GCCInstallation.getMultilibs(); + SelectedMultilib = GCCInstallation.getMultilib(); + + GCCLibAndIncVersion = GCCInstallation.getVersion().Text; + GCCToolchainName = GCCInstallation.getTriple().str(); + SmallString<128> Path(GCCInstallation.getParentLibPath()); + llvm::sys::path::append(Path, ".."); + GCCToolchainDir = Path.c_str(); + + SmallString<128> Libs1(GCCToolchainDir); + llvm::sys::path::append(Libs1, "lib", "gcc", GCCToolchainName, + GCCLibAndIncVersion); + if (!SelectedMultilib.gccSuffix().empty()) + llvm::sys::path::append(Libs1, SelectedMultilib.gccSuffix()); + getFilePaths().push_back(Libs1.c_str()); + + SmallString<128> Libs2(GCCToolchainDir); + llvm::sys::path::append(Libs2, GCCToolchainName, "lib"); + if (!SelectedMultilib.gccSuffix().empty()) + llvm::sys::path::append(Libs2, SelectedMultilib.gccSuffix()); + getFilePaths().push_back(Libs2.c_str()); + + ToolChain::path_list &PPaths = getProgramPaths(); + // Multilib cross-compiler GCC installations put ld in a triple-prefixed + // directory of the GCC installation parent dir. + StringRef ParentDir = llvm::sys::path::parent_path(GCCInstallation.getParentLibPath()); + + SmallString<128> PathTripleBin(ParentDir); + llvm::sys::path::append(PathTripleBin, GCCInstallation.getTriple().str()); + llvm::sys::path::append(PathTripleBin, "bin"); + PPaths.push_back(PathTripleBin.c_str()); + + SmallString<128> PathBin(ParentDir); + llvm::sys::path::append(PathBin, "bin"); + PPaths.push_back(PathBin.c_str()); + + if (!getDriver().SysRoot.empty()) { + SmallString<128> SysRoot(computeSysRoot()); + llvm::sys::path::append(SysRoot, "lib"); + getFilePaths().push_back(SysRoot.c_str()); + } + } else { + getProgramPaths().push_back(D.Dir); + SmallString<128> SysRoot(computeSysRoot()); + llvm::sys::path::append(SysRoot, "lib"); + getFilePaths().push_back(SysRoot.c_str()); + } +} + +Tool *XtensaToolChain::buildLinker() const { + return new tools::xtensa::Linker(*this); +} + +Tool *XtensaToolChain::buildAssembler() const { + return new tools::xtensa::Assembler(*this); +} + +void XtensaToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, + ArgStringList &CC1Args) const { + if (DriverArgs.hasArg(clang::driver::options::OPT_nostdinc) || + DriverArgs.hasArg(options::OPT_nostdlibinc)) + return; + + if (!getDriver().SysRoot.empty()) { + SmallString<128> Dir(getDriver().SysRoot); + llvm::sys::path::append(Dir, "include"); + addSystemInclude(DriverArgs, CC1Args, Dir.str()); + } else if (GCCInstallation.isValid()) { + SmallString<128> Path1(getDriver().ResourceDir); + llvm::sys::path::append(Path1, "include"); + SmallString<128> Path2(GCCToolchainDir); + llvm::sys::path::append(Path2, GCCToolchainName, "sys-include"); + SmallString<128> Path3(GCCToolchainDir); + llvm::sys::path::append(Path3, GCCToolchainName, "include"); + + const StringRef Paths[] = {Path1, Path2, Path3}; + addSystemIncludes(DriverArgs, CC1Args, Paths); + } else { + SmallString<128> Dir(computeSysRoot()); + llvm::sys::path::append(Dir, "include"); + addSystemInclude(DriverArgs, CC1Args, Dir.str()); + } +} + +void XtensaToolChain::addLibStdCxxIncludePaths( + const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const { + if (!GCCInstallation.isValid()) + return; + + const GCCVersion &Version = GCCInstallation.getVersion(); + StringRef TripleStr = GCCInstallation.getTriple().str(); + addLibStdCXXIncludePaths(computeSysRoot() + "/include/c++/" + Version.Text, + TripleStr, "", DriverArgs, CC1Args); +} + +std::string XtensaToolChain::computeSysRoot() const { + if (!getDriver().SysRoot.empty()) + return getDriver().SysRoot; + + SmallString<128> SysRootDir; + if (GCCInstallation.isValid()) { + StringRef LibDir = GCCInstallation.getParentLibPath(); + StringRef TripleStr = GCCInstallation.getTriple().str(); + llvm::sys::path::append(SysRootDir, LibDir, "..", TripleStr); + } else { + // Use the triple as provided to the driver. Unlike the parsed triple + // this has not been normalized to always contain every field. + llvm::sys::path::append(SysRootDir, getDriver().Dir, "..", + getDriver().getTargetTriple()); + } + + if (!llvm::sys::fs::exists(SysRootDir)) + return std::string(); + + return std::string(SysRootDir.str()); +} + +ToolChain::CXXStdlibType +XtensaToolChain::GetCXXStdlibType(const ArgList &Args) const { + Arg *A = Args.getLastArg(options::OPT_stdlib_EQ); + if (!A) + return ToolChain::CST_Libstdcxx; + + StringRef Value = A->getValue(); + if (Value != "libstdc++") + getDriver().Diag(diag::err_drv_invalid_stdlib_name) << A->getAsString(Args); + + return ToolChain::CST_Libstdcxx; +} + +ToolChain::UnwindLibType +XtensaToolChain::GetUnwindLibType(const llvm::opt::ArgList &Args) const { + return ToolChain::UNW_None; +} + +const StringRef XtensaToolChain::GetTargetCPUVersion(const ArgList &Args) { + if (Arg *A = Args.getLastArg(clang::driver::options::OPT_mcpu_EQ)) { + StringRef CPUName = A->getValue(); + return CPUName; + } + return "esp32"; +} + +void tools::xtensa::Assembler::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + const auto &TC = + static_cast(getToolChain()); + + claimNoWarnArgs(Args); + ArgStringList CmdArgs; + + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + + CmdArgs.push_back("-c"); + + if (Args.hasArg(options::OPT_v)) + CmdArgs.push_back("-v"); + + if (Arg *A = Args.getLastArg(options::OPT_g_Group)) + if (!A->getOption().matches(options::OPT_g0)) + CmdArgs.push_back("-g"); + + if (Args.hasFlag(options::OPT_fverbose_asm, options::OPT_fno_verbose_asm, + false)) + CmdArgs.push_back("-fverbose-asm"); + + Args.AddAllArgValues(CmdArgs, options::OPT_Wa_COMMA, options::OPT_Xassembler); + + for (const auto &II : Inputs) + CmdArgs.push_back(II.getFilename()); + + SmallString<128> Asm(TC.GCCToolchainDir); + llvm::sys::path::append(Asm, "bin", + TC.GCCToolchainName + "-" + getShortName()); + + C.addCommand( + std::make_unique(JA, *this, ResponseFileSupport::AtFileCurCP(), + Args.MakeArgString(Asm), CmdArgs, Inputs)); +} + +void xtensa::Linker::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + ArgStringList CmdArgs; + SmallString<128> Linker; + bool WantCRTs = + !Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles); + const auto &ToolChain = + static_cast(getToolChain()); + const Driver &D = ToolChain.getDriver(); + + if (!D.SysRoot.empty()) + CmdArgs.push_back(Args.MakeArgString("--sysroot=" + D.SysRoot)); + + bool LinkerIsLLD; + std::string LinkerPath = ToolChain.GetLinkerPath(&LinkerIsLLD); + if (ToolChain.GCCToolchainName != "") { + if (!LinkerIsLLD) { + Linker.assign(ToolChain.GCCToolchainDir); + llvm::sys::path::append( + Linker, "bin", ToolChain.GCCToolchainName + "-" + getShortName()); + } else { + Linker.assign(LinkerPath); + } + } else { + Linker.assign(LinkerPath); + } + + const char *crtbegin, *crtend; + auto RuntimeLib = ToolChain.GetRuntimeLibType(Args); + if (RuntimeLib == ToolChain::RLT_Libgcc) { + crtbegin = "crtbegin.o"; + crtend = "crtend.o"; + } else { + assert(RuntimeLib == ToolChain::RLT_CompilerRT); + crtbegin = ToolChain.getCompilerRTArgString(Args, "crtbegin", + ToolChain::FT_Object); + crtend = + ToolChain.getCompilerRTArgString(Args, "crtend", ToolChain::FT_Object); + } + + if (WantCRTs) { + // TODO: The crt0.o is not used for esp targets, but maybe used in + // future for other vendors + // CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crt0.o"))); + CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crtbegin))); + } + + AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA); + + Args.AddAllArgs(CmdArgs, options::OPT_L); + ToolChain.AddFilePathLibArgs(Args, CmdArgs); + Args.AddAllArgs(CmdArgs, + {options::OPT_T_Group, options::OPT_e, options::OPT_s, + options::OPT_t, options::OPT_u_Group}); + + if (!Args.hasArg(options::OPT_nostdlib) && + !Args.hasArg(options::OPT_nodefaultlibs)) { + if (ToolChain.ShouldLinkCXXStdlib(Args)) + ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs); + AddRunTimeLibs(ToolChain, ToolChain.getDriver(), CmdArgs, Args); + } + + if (WantCRTs) + CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crtend))); + + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + C.addCommand( + std::make_unique(JA, *this, ResponseFileSupport::AtFileCurCP(), + Args.MakeArgString(Linker), CmdArgs, Inputs)); +} + +// Get features by CPU name +static void getXtensaFeaturesFromMcpu(const Driver &D, + const llvm::opt::ArgList &Args, + const llvm::opt::Arg *A, StringRef Mcpu, + std::vector &Features) { + if (llvm::Xtensa::parseCPUKind(Mcpu) == llvm::Xtensa::CK_INVALID) { + D.Diag(clang::diag::err_drv_clang_unsupported) << A->getAsString(Args); + } else { + SmallVector CPUFeatures; + llvm::Xtensa::getCPUFeatures(Mcpu, CPUFeatures); + for (auto &F : CPUFeatures) { + Features.push_back(F); + } + } +} + +// Xtensa target features. +void xtensa::getXtensaTargetFeatures(const Driver &D, const ArgList &Args, + std::vector &Features) { + if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) + getXtensaFeaturesFromMcpu(D, Args, A, A->getValue(), Features); +} diff --git a/clang/lib/Driver/ToolChains/Xtensa.h b/clang/lib/Driver/ToolChains/Xtensa.h new file mode 100644 index 0000000000000..8f3ed74923ba8 --- /dev/null +++ b/clang/lib/Driver/ToolChains/Xtensa.h @@ -0,0 +1,90 @@ +//===--- Xtensa.h - Xtensa Tool and ToolChain Implementations ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_Xtensa_H +#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_Xtensa_H + +#include "Gnu.h" +#include "clang/Driver/InputInfo.h" +#include "clang/Driver/Tool.h" +#include "clang/Driver/ToolChain.h" + +namespace clang { +namespace driver { +namespace toolchains { + +class LLVM_LIBRARY_VISIBILITY XtensaToolChain : public Generic_ELF { +protected: + Tool *buildLinker() const override; + Tool *buildAssembler() const override; + +public: + XtensaToolChain(const Driver &D, const llvm::Triple &Triple, + const llvm::opt::ArgList &Args); + void + AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const override; + void + addLibStdCxxIncludePaths(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const override; + CXXStdlibType GetCXXStdlibType(const llvm::opt::ArgList &Args) const override; + UnwindLibType GetUnwindLibType(const llvm::opt::ArgList &Args) const override; + + bool IsIntegratedAssemblerDefault() const override { + return (IsIntegratedAsm || (GCCToolchainName == "")); + } + + static const StringRef GetTargetCPUVersion(const llvm::opt::ArgList &Args); + + bool IsIntegratedAsm = true; + std::string GCCLibAndIncVersion = ""; + std::string GCCToolchainName = ""; + std::string GCCToolchainDir = ""; + +private: + std::string computeSysRoot() const override; +}; + +} // end namespace toolchains + +namespace tools { +namespace xtensa { +class LLVM_LIBRARY_VISIBILITY Linker : public Tool { +public: + Linker(const ToolChain &TC) + : Tool("Xtensa::Linker", "ld", TC) {} + bool hasIntegratedCPP() const override { return false; } + bool isLinkJob() const override { return true; } + void ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, const InputInfoList &Inputs, + const llvm::opt::ArgList &TCArgs, + const char *LinkingOutput) const override; +}; + +class LLVM_LIBRARY_VISIBILITY Assembler : public Tool { +public: + Assembler(const ToolChain &TC) + : Tool("Xtensa::Assembler", "as", TC) {} + + bool hasIntegratedCPP() const override { return false; } + void ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, const InputInfoList &Inputs, + const llvm::opt::ArgList &TCArgs, + const char *LinkingOutput) const override; +}; + +void getXtensaTargetFeatures(const Driver &D, const llvm::opt::ArgList &Args, + std::vector &Features); +} // end namespace xtensa +} // end namespace tools +} // end namespace driver +} // end namespace clang + +#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_Xtensa_H diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index dae51d0690e6d..6b646dd02c0d2 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1981,6 +1981,8 @@ bool Sema::CheckTSBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, case llvm::Triple::riscv32: case llvm::Triple::riscv64: return CheckRISCVBuiltinFunctionCall(TI, BuiltinID, TheCall); + case llvm::Triple::xtensa: + return CheckXtensaBuiltinFunctionCall(BuiltinID, TheCall); } } @@ -4514,6 +4516,83 @@ bool Sema::CheckSystemZBuiltinFunctionCall(unsigned BuiltinID, return SemaBuiltinConstantArgRange(TheCall, i, l, u); } +bool Sema::CheckXtensaBuiltinFunctionCall(unsigned BuiltinID, + CallExpr *TheCall) { + unsigned i = 0, l = 0, u = 0; + switch (BuiltinID) { + default: + return false; + case Xtensa::BI__builtin_xtensa_mul_ad_ll: + case Xtensa::BI__builtin_xtensa_mul_ad_lh: + case Xtensa::BI__builtin_xtensa_mul_ad_hl: + case Xtensa::BI__builtin_xtensa_mul_ad_hh: + case Xtensa::BI__builtin_xtensa_mula_ad_ll: + case Xtensa::BI__builtin_xtensa_mula_ad_lh: + case Xtensa::BI__builtin_xtensa_mula_ad_hl: + case Xtensa::BI__builtin_xtensa_mula_ad_hh: + case Xtensa::BI__builtin_xtensa_muls_ad_ll: + case Xtensa::BI__builtin_xtensa_muls_ad_lh: + case Xtensa::BI__builtin_xtensa_muls_ad_hl: + case Xtensa::BI__builtin_xtensa_muls_ad_hh: + i = 1; + l = 2; + u = 3; + break; + case Xtensa::BI__builtin_xtensa_mul_da_ll: + case Xtensa::BI__builtin_xtensa_mul_da_lh: + case Xtensa::BI__builtin_xtensa_mul_da_hl: + case Xtensa::BI__builtin_xtensa_mul_da_hh: + case Xtensa::BI__builtin_xtensa_mula_da_ll: + case Xtensa::BI__builtin_xtensa_mula_da_lh: + case Xtensa::BI__builtin_xtensa_mula_da_hl: + case Xtensa::BI__builtin_xtensa_mula_da_hh: + case Xtensa::BI__builtin_xtensa_muls_da_ll: + case Xtensa::BI__builtin_xtensa_muls_da_lh: + case Xtensa::BI__builtin_xtensa_muls_da_hl: + case Xtensa::BI__builtin_xtensa_muls_da_hh: + i = 0; + l = 0; + u = 1; + break; + case Xtensa::BI__builtin_xtensa_mul_dd_ll: + case Xtensa::BI__builtin_xtensa_mul_dd_lh: + case Xtensa::BI__builtin_xtensa_mul_dd_hl: + case Xtensa::BI__builtin_xtensa_mul_dd_hh: + case Xtensa::BI__builtin_xtensa_mula_dd_ll: + case Xtensa::BI__builtin_xtensa_mula_dd_lh: + case Xtensa::BI__builtin_xtensa_mula_dd_hl: + case Xtensa::BI__builtin_xtensa_mula_dd_hh: + case Xtensa::BI__builtin_xtensa_muls_dd_ll: + case Xtensa::BI__builtin_xtensa_muls_dd_lh: + case Xtensa::BI__builtin_xtensa_muls_dd_hl: + case Xtensa::BI__builtin_xtensa_muls_dd_hh: + return SemaBuiltinConstantArgRange(TheCall, 0, 0, 1) || + SemaBuiltinConstantArgRange(TheCall, 1, 2, 3); + case Xtensa::BI__builtin_xtensa_mula_da_ll_lddec: + case Xtensa::BI__builtin_xtensa_mula_da_lh_lddec: + case Xtensa::BI__builtin_xtensa_mula_da_hl_lddec: + case Xtensa::BI__builtin_xtensa_mula_da_hh_lddec: + case Xtensa::BI__builtin_xtensa_mula_da_ll_ldinc: + case Xtensa::BI__builtin_xtensa_mula_da_lh_ldinc: + case Xtensa::BI__builtin_xtensa_mula_da_hl_ldinc: + case Xtensa::BI__builtin_xtensa_mula_da_hh_ldinc: + return SemaBuiltinConstantArgRange(TheCall, 0, 0, 3) || + SemaBuiltinConstantArgRange(TheCall, 2, 0, 1); + case Xtensa::BI__builtin_xtensa_mula_dd_ll_lddec: + case Xtensa::BI__builtin_xtensa_mula_dd_lh_lddec: + case Xtensa::BI__builtin_xtensa_mula_dd_hl_lddec: + case Xtensa::BI__builtin_xtensa_mula_dd_hh_lddec: + case Xtensa::BI__builtin_xtensa_mula_dd_ll_ldinc: + case Xtensa::BI__builtin_xtensa_mula_dd_lh_ldinc: + case Xtensa::BI__builtin_xtensa_mula_dd_hl_ldinc: + case Xtensa::BI__builtin_xtensa_mula_dd_hh_ldinc: + return SemaBuiltinConstantArgRange(TheCall, 0, 0, 3) || + SemaBuiltinConstantArgRange(TheCall, 2, 0, 1) || + SemaBuiltinConstantArgRange(TheCall, 3, 2, 3); + } + return SemaBuiltinConstantArgRange(TheCall, i, l, u); +} + /// SemaBuiltinCpuSupports - Handle __builtin_cpu_supports(char *). /// This checks that the target supports __builtin_cpu_supports and /// that the string argument is constant and valid. diff --git a/clang/test/CodeGen/xtensa-abi.c b/clang/test/CodeGen/xtensa-abi.c new file mode 100644 index 0000000000000..d50a45bcde489 --- /dev/null +++ b/clang/test/CodeGen/xtensa-abi.c @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -no-opaque-pointers -triple xtensa -O0 -emit-llvm %s -o - | FileCheck %s + +#define __malloc_like __attribute__((__malloc__)) +char *bufalloc () __malloc_like ;//__result_use_check; +extern void* malloc (unsigned size); + +char *bufalloc () +{ + char* buf = malloc(1024); + + return buf; +} + +// CHECK: define dso_local noalias i8* @bufalloc() #0 { + +struct S16 { int a[4]; } __attribute__ ((aligned (16))); + +void callee_struct_a16b_1(struct S16 a) {} + +// CHECK: define dso_local void @callee_struct_a16b_1(i128 %a.coerce) + +void callee_struct_a16b_2(struct S16 a, int b) {} + +// CHECK: define dso_local void @callee_struct_a16b_2(i128 %a.coerce, i32 noundef %b) + +void callee_struct_a16b_3(int a, struct S16 b) {} + +// CHECK: define dso_local void @callee_struct_a16b_3(i32 noundef %a, %struct.S16* noundef byval(%struct.S16) align 16 %b) diff --git a/clang/test/CodeGen/xtensa-inline-asm.c b/clang/test/CodeGen/xtensa-inline-asm.c new file mode 100644 index 0000000000000..9c7473eb98359 --- /dev/null +++ b/clang/test/CodeGen/xtensa-inline-asm.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -no-opaque-pointers -triple xtensa -O1 -emit-llvm %s -o - \ +// RUN: | FileCheck %s + +// Test Xtensa specific inline assembly constraints. + +float f; +void test_f() { +// CHECK-LABEL: define dso_local void @test_f() local_unnamed_addr #0 { +// CHECK: [[FLT_ARG:%[a-zA-Z_0-9]+]] = load float, float* @f +// CHECK: call void asm sideeffect "", "f"(float [[FLT_ARG]]) + asm volatile ("" :: "f"(f)); +} + diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/bin/riscv32-esp-elf-as b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/bin/riscv32-esp-elf-as new file mode 100755 index 0000000000000..b23e55619b2ff --- /dev/null +++ b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/bin/riscv32-esp-elf-as @@ -0,0 +1 @@ +#!/bin/true diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/bin/riscv32-esp-elf-ld b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/bin/riscv32-esp-elf-ld new file mode 100755 index 0000000000000..b23e55619b2ff --- /dev/null +++ b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/bin/riscv32-esp-elf-ld @@ -0,0 +1 @@ +#!/bin/true diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/crtend.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32i/ilp32/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32i/ilp32/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32i/ilp32/crtend.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32i/ilp32/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32i/ilp32/no-rtti/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32i/ilp32/no-rtti/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32i/ilp32/no-rtti/crtend.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32i/ilp32/no-rtti/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32/crtend.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32/no-rtti/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32/no-rtti/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32/no-rtti/crtend.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32/no-rtti/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imafc/ilp32f/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imafc/ilp32f/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imafc/ilp32f/crtend.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imafc/ilp32f/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imafc/ilp32f/no-rtti/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imafc/ilp32f/no-rtti/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imafc/ilp32f/no-rtti/crtend.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imafc/ilp32f/no-rtti/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imc/ilp32/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imc/ilp32/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imc/ilp32/crtend.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imc/ilp32/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imc/ilp32/no-rtti/crtbegin.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imc/ilp32/no-rtti/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imc/ilp32/no-rtti/crtend.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imc/ilp32/no-rtti/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/include/c++/8.4.0/.keep b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/include/c++/8.4.0/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/crt0.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32i/ilp32/crt0.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32i/ilp32/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32i/ilp32/no-rtti/crt0.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32i/ilp32/no-rtti/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32imac/ilp32/crt0.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32imac/ilp32/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32imac/ilp32/no-rtti/crt0.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32imac/ilp32/no-rtti/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32imafc/ilp32f/crt0.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32imafc/ilp32f/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32imafc/ilp32f/no-rtti/crt0.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32imafc/ilp32f/no-rtti/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32imc/ilp32/crt0.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32imc/ilp32/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32imc/ilp32/no-rtti/crt0.o b/clang/test/Driver/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib/rv32imc/ilp32/no-rtti/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/bin/xtensa-esp32-elf-ld b/clang/test/Driver/Inputs/multilib_xtensa_tree/bin/xtensa-esp32-elf-ld new file mode 100755 index 0000000000000..b23e55619b2ff --- /dev/null +++ b/clang/test/Driver/Inputs/multilib_xtensa_tree/bin/xtensa-esp32-elf-ld @@ -0,0 +1 @@ +#!/bin/true diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/bin/xtensa-esp32s2-elf-ld b/clang/test/Driver/Inputs/multilib_xtensa_tree/bin/xtensa-esp32s2-elf-ld new file mode 100755 index 0000000000000..b23e55619b2ff --- /dev/null +++ b/clang/test/Driver/Inputs/multilib_xtensa_tree/bin/xtensa-esp32s2-elf-ld @@ -0,0 +1 @@ +#!/bin/true diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/bin/xtensa-esp32s3-elf-ld b/clang/test/Driver/Inputs/multilib_xtensa_tree/bin/xtensa-esp32s3-elf-ld new file mode 100755 index 0000000000000..b23e55619b2ff --- /dev/null +++ b/clang/test/Driver/Inputs/multilib_xtensa_tree/bin/xtensa-esp32s3-elf-ld @@ -0,0 +1 @@ +#!/bin/true diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/crtbegin.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/crtend.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/esp32-psram/crtbegin.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/esp32-psram/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/esp32-psram/crtend.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/esp32-psram/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/esp32-psram/no-rtti/crtbegin.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/esp32-psram/no-rtti/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/esp32-psram/no-rtti/crtend.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/esp32-psram/no-rtti/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/no-rtti/crtbegin.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/no-rtti/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/no-rtti/crtend.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/no-rtti/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/crtbegin.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/crtend.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/no-rtti/crtbegin.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/no-rtti/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/no-rtti/crtend.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/no-rtti/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/crtbegin.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/crtend.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/no-rtti/crtbegin.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/no-rtti/crtbegin.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/no-rtti/crtend.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/no-rtti/crtend.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32-elf/include/c++/8.4.0/.keep b/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32-elf/include/c++/8.4.0/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32-elf/lib/crt0.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32-elf/lib/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32-elf/lib/esp32-psram/crt0.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32-elf/lib/esp32-psram/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32-elf/lib/esp32-psram/no-rtti/crt0.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32-elf/lib/esp32-psram/no-rtti/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32-elf/lib/no-rtti/crt0.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32-elf/lib/no-rtti/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32s2-elf/lib/crt0.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32s2-elf/lib/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32s2-elf/lib/no-rtti/crt0.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32s2-elf/lib/no-rtti/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32s3-elf/lib/crt0.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32s3-elf/lib/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32s3-elf/lib/no-rtti/crt0.o b/clang/test/Driver/Inputs/multilib_xtensa_tree/xtensa-esp32s3-elf/lib/no-rtti/crt0.o new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/Driver/riscv32-esp-toolchain.c b/clang/test/Driver/riscv32-esp-toolchain.c new file mode 100644 index 0000000000000..34ef6871f30f0 --- /dev/null +++ b/clang/test/Driver/riscv32-esp-toolchain.c @@ -0,0 +1,325 @@ +// A basic clang -cc1 command-line, and simple environment check. + +// RUN: %clang %s -### -no-canonical-prefixes -target riscv32-esp-elf \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_riscv_esp_elf_sdk 2>&1 \ +// RUN: | FileCheck -check-prefix=CC1 %s +// CC1: clang{{.*}} "-cc1" "-triple" "riscv32-esp-unknown-elf" + +// Test interaction with -fuse-ld=lld, if lld is available. +// RUN: %clang %s -### -no-canonical-prefixes -target riscv32-esp-elf \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_riscv_esp_elf_sdk -fuse-ld=lld 2>&1 \ +// RUN: | FileCheck -check-prefix=LLD %s +// LLD: {{(error: invalid linker name in argument '-fuse-ld=lld')|(ld.lld)}} + +// rv32imac is the default + +// RUN: %clang %s -### -no-canonical-prefixes -target riscv32-esp-elf \ +// RUN: -ffreestanding --rtlib=libgcc --ld-path=riscv32-esp-elf-ld \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_riscv_esp_elf_sdk \ +// RUN: --sysroot=%S/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf 2>&1 \ +// RUN: | FileCheck -check-prefix=C-RV32IMAC-BAREMETAL-MULTI-ILP32 %s + +// C-RV32IMAC-BAREMETAL-MULTI-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-as" +// C-RV32IMAC-BAREMETAL-MULTI-ILP32: "-mabi" "ilp32" "-march" "rv32imac" +// C-RV32IMAC-BAREMETAL-MULTI-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-ld" +// C-RV32IMAC-BAREMETAL-MULTI-ILP32: "--sysroot={{.*}}/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf" +// C-RV32IMAC-BAREMETAL-MULTI-ILP32: "-m" "elf32lriscv" +// C-RV32IMAC-BAREMETAL-MULTI-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32{{/|\\\\}}crtbegin.o" +// C-RV32IMAC-BAREMETAL-MULTI-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0" +// C-RV32IMAC-BAREMETAL-MULTI-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib" +// C-RV32IMAC-BAREMETAL-MULTI-ILP32: "--start-group" "-lc" "-lgloss" "-lnosys" "--end-group" "-lgcc" +// C-RV32IMAC-BAREMETAL-MULTI-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32{{/|\\\\}}crtend.o" + +// RUN: %clang %s -### -no-canonical-prefixes -target riscv32-esp-elf \ +// RUN: -ffreestanding --rtlib=libgcc --ld-path=riscv32-esp-elf-ld \ +// RUN: --sysroot= \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_riscv_esp_elf_sdk 2>&1 \ +// RUN: | FileCheck -check-prefix=C-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32 %s + +// C-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-as" +// C-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "-mabi" "ilp32" "-march" "rv32imac" +// C-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-ld" +// C-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "-m" "elf32lriscv" +// C-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32{{/|\\\\}}crtbegin.o" +// C-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0" +// C-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}riscv32-esp-elf/lib" +// C-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "--start-group" "-lc" "-lgloss" "-lnosys" "--end-group" "-lgcc" +// C-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32{{/|\\\\}}crtend.o" + +// RUN: %clangxx %s -### -no-canonical-prefixes -target riscv32-esp-elf \ +// RUN: -ffreestanding -stdlib=libstdc++ --rtlib=libgcc --ld-path=riscv32-esp-elf-ld \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_riscv_esp_elf_sdk \ +// RUN: --sysroot=%S/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf 2>&1 \ +// RUN: | FileCheck -check-prefix=CXX-RV32IMAC-BAREMETAL-MULTI-ILP32 %s + +// CXX-RV32IMAC-BAREMETAL-MULTI-ILP32: "-internal-isystem" "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/include/c++{{/|\\\\}}8.4.0" +// CXX-RV32IMAC-BAREMETAL-MULTI-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-as" +// CXX-RV32IMAC-BAREMETAL-MULTI-ILP32: "-mabi" "ilp32" "-march" "rv32imac" +// CXX-RV32IMAC-BAREMETAL-MULTI-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-ld" +// CXX-RV32IMAC-BAREMETAL-MULTI-ILP32: "--sysroot={{.*}}/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf" +// CXX-RV32IMAC-BAREMETAL-MULTI-ILP32: "-m" "elf32lriscv" +// CXX-RV32IMAC-BAREMETAL-MULTI-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32{{/|\\\\}}crtbegin.o" +// CXX-RV32IMAC-BAREMETAL-MULTI-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0" +// CXX-RV32IMAC-BAREMETAL-MULTI-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib" +// CXX-RV32IMAC-BAREMETAL-MULTI-ILP32: "-lstdc++" "-lm" "--start-group" "-lc" "-lgloss" "-lnosys" "--end-group" "-lgcc" +// CXX-RV32IMAC-BAREMETAL-MULTI-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32{{/|\\\\}}crtend.o" + +// RUN: %clangxx %s -### -no-canonical-prefixes -target riscv32-esp-elf \ +// RUN: -ffreestanding -stdlib=libstdc++ --rtlib=libgcc --ld-path=riscv32-esp-elf-ld \ +// RUN: --sysroot= \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_riscv_esp_elf_sdk 2>&1 \ +// RUN: | FileCheck -check-prefix=CXX-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32 %s + +// CXX-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "-internal-isystem" "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}riscv32-esp-elf/include/c++{{/|\\\\}}8.4.0" +// CXX-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-as" +// CXX-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "-mabi" "ilp32" "-march" "rv32imac" +// CXX-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-ld" +// CXX-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "-m" "elf32lriscv" +// CXX-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32{{/|\\\\}}crtbegin.o" +// CXX-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0" +// CXX-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}riscv32-esp-elf/lib" +// CXX-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "-lstdc++" "-lm" "--start-group" "-lc" "-lgloss" "-lnosys" "--end-group" "-lgcc" +// CXX-RV32IMAC-BAREMETAL-MULTI-NOSYSROOT-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32{{/|\\\\}}crtend.o" + +// RUN: %clangxx %s -### -no-canonical-prefixes -target riscv32-esp-elf \ +// RUN: -ffreestanding -stdlib=libstdc++ --rtlib=libgcc --ld-path=riscv32-esp-elf-ld \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_riscv_esp_elf_sdk \ +// RUN: --sysroot=%S/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf \ +// RUN: -fno-rtti 2>&1 \ +// RUN: | FileCheck -check-prefix=CXX-RV32IMAC-BAREMETAL-MULTI-NORTTI-ILP32 %s + +// CXX-RV32IMAC-BAREMETAL-MULTI-NORTTI-ILP32: "-internal-isystem" "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/include/c++{{/|\\\\}}8.4.0" +// CXX-RV32IMAC-BAREMETAL-MULTI-NORTTI-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-as" +// CXX-RV32IMAC-BAREMETAL-MULTI-NORTTI-ILP32: "-mabi" "ilp32" "-march" "rv32imac" +// CXX-RV32IMAC-BAREMETAL-MULTI-NORTTI-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-ld" +// CXX-RV32IMAC-BAREMETAL-MULTI-NORTTI-ILP32: "--sysroot={{.*}}/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf" +// CXX-RV32IMAC-BAREMETAL-MULTI-NORTTI-ILP32: "-m" "elf32lriscv" +// CXX-RV32IMAC-BAREMETAL-MULTI-NORTTI-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32/no-rtti{{/|\\\\}}crtbegin.o" +// CXX-RV32IMAC-BAREMETAL-MULTI-NORTTI-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0" +// CXX-RV32IMAC-BAREMETAL-MULTI-NORTTI-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/riscv32-esp-elf/lib" +// CXX-RV32IMAC-BAREMETAL-MULTI-NORTTI-ILP32: "-lstdc++" "-lm" "--start-group" "-lc" "-lgloss" "-lnosys" "--end-group" "-lgcc" +// CXX-RV32IMAC-BAREMETAL-MULTI-NORTTI-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imac/ilp32/no-rtti{{/|\\\\}}crtend.o" + +// RUN: %clang %s -### -no-canonical-prefixes -target riscv32-esp-elf \ +// RUN: -march=rv32i -mabi=ilp32 \ +// RUN: -ffreestanding --rtlib=libgcc --ld-path=riscv32-esp-elf-ld --sysroot= \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_riscv_esp_elf_sdk 2>&1 \ +// RUN: | FileCheck -check-prefix=C-RV32I-BAREMETAL-MULTI-ILP32 %s + +// C-RV32I-BAREMETAL-MULTI-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-as" +// C-RV32I-BAREMETAL-MULTI-ILP32: "-mabi" "ilp32" "-march" "rv32i" +// C-RV32I-BAREMETAL-MULTI-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-ld" +// C-RV32I-BAREMETAL-MULTI-ILP32: "-m" "elf32lriscv" +// C-RV32I-BAREMETAL-MULTI-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32i/ilp32{{/|\\\\}}crtbegin.o" +// C-RV32I-BAREMETAL-MULTI-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0" +// C-RV32I-BAREMETAL-MULTI-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}riscv32-esp-elf/lib" +// C-RV32I-BAREMETAL-MULTI-ILP32: "--start-group" "-lc" "-lgloss" "-lnosys" "--end-group" "-lgcc" +// C-RV32I-BAREMETAL-MULTI-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32i/ilp32{{/|\\\\}}crtend.o" + +// RUN: %clang %s -### -no-canonical-prefixes -target riscv32-esp-elf \ +// RUN: -march=rv32imc -mabi=ilp32 \ +// RUN: -ffreestanding --rtlib=libgcc --ld-path=riscv32-esp-elf-ld --sysroot= \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_riscv_esp_elf_sdk 2>&1 \ +// RUN: | FileCheck -check-prefix=C-RV32IMC-BAREMETAL-MULTI-ILP32 %s + +// C-RV32IMC-BAREMETAL-MULTI-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-as" +// C-RV32IMC-BAREMETAL-MULTI-ILP32: "-mabi" "ilp32" "-march" "rv32imc" +// C-RV32IMC-BAREMETAL-MULTI-ILP32: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-ld" +// C-RV32IMC-BAREMETAL-MULTI-ILP32: "-m" "elf32lriscv" +// C-RV32IMC-BAREMETAL-MULTI-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imc/ilp32{{/|\\\\}}crtbegin.o" +// C-RV32IMC-BAREMETAL-MULTI-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0" +// C-RV32IMC-BAREMETAL-MULTI-ILP32: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}riscv32-esp-elf/lib" +// C-RV32IMC-BAREMETAL-MULTI-ILP32: "--start-group" "-lc" "-lgloss" "-lnosys" "--end-group" "-lgcc" +// C-RV32IMC-BAREMETAL-MULTI-ILP32: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imc/ilp32{{/|\\\\}}crtend.o" + +// RUN: %clang %s -### -no-canonical-prefixes -target riscv32-esp-elf \ +// RUN: -march=rv32imafc -mabi=ilp32f \ +// RUN: -ffreestanding --rtlib=libgcc --ld-path=riscv32-esp-elf-ld --sysroot= \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_riscv_esp_elf_sdk 2>&1 \ +// RUN: | FileCheck -check-prefix=C-RV32IMAFC-BAREMETAL-MULTI-ILP32F %s + +// C-RV32IMAFC-BAREMETAL-MULTI-ILP32F: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-as" +// C-RV32IMAFC-BAREMETAL-MULTI-ILP32F: "-mabi" "ilp32f" "-march" "rv32imafc" +// C-RV32IMAFC-BAREMETAL-MULTI-ILP32F: "{{.*}}Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}riscv32-esp-elf-ld" +// C-RV32IMAFC-BAREMETAL-MULTI-ILP32F: "-m" "elf32lriscv" +// C-RV32IMAFC-BAREMETAL-MULTI-ILP32F: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imafc/ilp32f{{/|\\\\}}crtbegin.o" +// C-RV32IMAFC-BAREMETAL-MULTI-ILP32F: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0" +// C-RV32IMAFC-BAREMETAL-MULTI-ILP32F: "-L{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}riscv32-esp-elf/lib" +// C-RV32IMAFC-BAREMETAL-MULTI-ILP32F: "--start-group" "-lc" "-lgloss" "-lnosys" "--end-group" "-lgcc" +// C-RV32IMAFC-BAREMETAL-MULTI-ILP32F: "{{.*}}/Inputs/multilib_riscv_esp_elf_sdk/lib/gcc/riscv32-esp-elf/8.4.0/rv32imafc/ilp32f{{/|\\\\}}crtend.o" + +// RUN: %clang -target riscv32-esp-elf %s -emit-llvm -S -o - | FileCheck %s + +typedef __builtin_va_list va_list; +typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef __WCHAR_TYPE__ wchar_t; +typedef __WINT_TYPE__ wint_t; + + +// Check Alignments + +// CHECK: @align_c = dso_local global i32 1 +int align_c = __alignof(char); + +// CHECK: @align_s = dso_local global i32 2 +int align_s = __alignof(short); + +// CHECK: @align_i = dso_local global i32 4 +int align_i = __alignof(int); + +// CHECK: @align_wc = dso_local global i32 4 +int align_wc = __alignof(wchar_t); + +// CHECK: @align_wi = dso_local global i32 4 +int align_wi = __alignof(wint_t); + +// CHECK: @align_l = dso_local global i32 4 +int align_l = __alignof(long); + +// CHECK: @align_ll = dso_local global i32 8 +int align_ll = __alignof(long long); + +// CHECK: @align_p = dso_local global i32 4 +int align_p = __alignof(void*); + +// CHECK: @align_f = dso_local global i32 4 +int align_f = __alignof(float); + +// CHECK: @align_d = dso_local global i32 8 +int align_d = __alignof(double); + +// CHECK: @align_ld = dso_local global i32 16 +int align_ld = __alignof(long double); + +// CHECK: @align_vl = dso_local global i32 4 +int align_vl = __alignof(va_list); + +// CHECK: @align_a_c = dso_local global i32 1 +int align_a_c = __alignof(_Atomic(char)); + +// CHECK: @align_a_s = dso_local global i32 2 +int align_a_s = __alignof(_Atomic(short)); + +// CHECK: @align_a_i = dso_local global i32 4 +int align_a_i = __alignof(_Atomic(int)); + +// CHECK: @align_a_wc = dso_local global i32 4 +int align_a_wc = __alignof(_Atomic(wchar_t)); + +// CHECK: @align_a_wi = dso_local global i32 4 +int align_a_wi = __alignof(_Atomic(wint_t)); + +// CHECK: @align_a_l = dso_local global i32 4 +int align_a_l = __alignof(_Atomic(long)); + +// CHECK: @align_a_ll = dso_local global i32 8 +int align_a_ll = __alignof(_Atomic(long long)); + +// CHECK: @align_a_p = dso_local global i32 4 +int align_a_p = __alignof(_Atomic(void*)); + +// CHECK: @align_a_f = dso_local global i32 4 +int align_a_f = __alignof(_Atomic(float)); + +// CHECK: @align_a_d = dso_local global i32 8 +int align_a_d = __alignof(_Atomic(double)); + +// CHECK: @align_a_ld = dso_local global i32 16 +int align_a_ld = __alignof(_Atomic(long double)); + +// CHECK: @align_a_s4 = dso_local global i32 4 +int align_a_s4 = __alignof(_Atomic(struct { char s[4]; })); + +// CHECK: @align_a_s8 = dso_local global i32 8 +int align_a_s8 = __alignof(_Atomic(struct { char s[8]; })); + +// CHECK: @align_a_s16 = dso_local global i32 16 +int align_a_s16 = __alignof(_Atomic(struct { char s[16]; })); + +// CHECK: @align_a_s32 = dso_local global i32 1 +int align_a_s32 = __alignof(_Atomic(struct { char s[32]; })); + + +// Check Sizes + +// CHECK: @size_a_c = dso_local global i32 1 +int size_a_c = sizeof(_Atomic(char)); + +// CHECK: @size_a_s = dso_local global i32 2 +int size_a_s = sizeof(_Atomic(short)); + +// CHECK: @size_a_i = dso_local global i32 4 +int size_a_i = sizeof(_Atomic(int)); + +// CHECK: @size_a_wc = dso_local global i32 4 +int size_a_wc = sizeof(_Atomic(wchar_t)); + +// CHECK: @size_a_wi = dso_local global i32 4 +int size_a_wi = sizeof(_Atomic(wint_t)); + +// CHECK: @size_a_l = dso_local global i32 4 +int size_a_l = sizeof(_Atomic(long)); + +// CHECK: @size_a_ll = dso_local global i32 8 +int size_a_ll = sizeof(_Atomic(long long)); + +// CHECK: @size_a_p = dso_local global i32 4 +int size_a_p = sizeof(_Atomic(void*)); + +// CHECK: @size_a_f = dso_local global i32 4 +int size_a_f = sizeof(_Atomic(float)); + +// CHECK: @size_a_d = dso_local global i32 8 +int size_a_d = sizeof(_Atomic(double)); + +// CHECK: @size_a_ld = dso_local global i32 16 +int size_a_ld = sizeof(_Atomic(long double)); + + +// Check types + +// CHECK: zeroext i8 @check_char() +char check_char() { return 0; } + +// CHECK: define dso_local signext i16 @check_short() +short check_short() { return 0; } + +// CHECK: define dso_local i32 @check_int() +int check_int() { return 0; } + +// CHECK: define dso_local i32 @check_wchar_t() +int check_wchar_t() { return 0; } + +// CHECK: define dso_local i32 @check_long() +long check_long() { return 0; } + +// CHECK: define dso_local i64 @check_longlong() +long long check_longlong() { return 0; } + +// CHECK: define dso_local zeroext i8 @check_uchar() +unsigned char check_uchar() { return 0; } + +// CHECK: define dso_local zeroext i16 @check_ushort() +unsigned short check_ushort() { return 0; } + +// CHECK: define dso_local i32 @check_uint() +unsigned int check_uint() { return 0; } + +// CHECK: define dso_local i32 @check_ulong() +unsigned long check_ulong() { return 0; } + +// CHECK: define dso_local i64 @check_ulonglong() +unsigned long long check_ulonglong() { return 0; } + +// CHECK: define dso_local i32 @check_size_t() +size_t check_size_t() { return 0; } + +// CHECK: define dso_local float @check_float() +float check_float() { return 0; } + +// CHECK: define dso_local double @check_double() +double check_double() { return 0; } + +// CHECK: define dso_local fp128 @check_longdouble() +long double check_longdouble() { return 0; } diff --git a/clang/test/Driver/xtensa-cpus.c b/clang/test/Driver/xtensa-cpus.c new file mode 100644 index 0000000000000..7a93f4cba4b0c --- /dev/null +++ b/clang/test/Driver/xtensa-cpus.c @@ -0,0 +1,36 @@ +// Check target CPUs are correctly passed. + +// RUN: %clang -target xtensa -### -c %s 2>&1 -mcpu=esp8266 | FileCheck -check-prefix=MCPU-ESP8266 %s +// MCPU-ESP8266: "-target-cpu" "esp8266" +// MCPU-ESP8266: "-target-feature" "+density" "-target-feature" "+nsa" "-target-feature" "+mul32" "-target-feature" "+extendedl32r" +// MCPU-ESP8266: "-target-feature" "+debug" "-target-feature" "+exception" "-target-feature" "+highpriinterrupts" +// MCPU-ESP8266: "-target-feature" "+interrupt" "-target-feature" "+rvector" "-target-feature" "+timerint" "-target-feature" "+prid" +// MCPU-ESP8266: "-target-feature" "+regprotect" + +// RUN: %clang -target xtensa -### -c %s 2>&1 -mcpu=esp32 | FileCheck -check-prefix=MCPU-ESP32 %s +// MCPU-ESP32: "-target-cpu" "esp32" +// MCPU-ESP32: "-target-feature" "+density" "-target-feature" "+fp" "-target-feature" "+windowed" "-target-feature" "+bool" +// MCPU-ESP32: "-target-feature" "+loop" "-target-feature" "+sext" "-target-feature" "+nsa" "-target-feature" "+mul32" +// MCPU-ESP32: "-target-feature" "+mul32high" "-target-feature" "+div32" "-target-feature" "+mac16" "-target-feature" "+dfpaccel" +// MCPU-ESP32: "-target-feature" "+s32c1i" "-target-feature" "+threadptr" "-target-feature" "+atomctl" "-target-feature" "+memctl" +// MCPU-ESP32: "-target-feature" "+debug" "-target-feature" "+exception" "-target-feature" "+highpriinterrupts" +// MCPU-ESP32: "-target-feature" "+coprocessor" "-target-feature" "+interrupt" "-target-feature" "+rvector" "-target-feature" "+timerint" +// MCPU-ESP32: "-target-feature" "+prid" "-target-feature" "+regprotect" "-target-feature" "+miscsr" + +// RUN: %clang -target xtensa -### -c %s 2>&1 -mcpu=esp32s2 | FileCheck -check-prefix=MCPU-ESP32S2 %s +// MCPU-ESP32S2: "-target-cpu" "esp32s2" +// MCPU-ESP32S2: "-target-feature" "+density" "-target-feature" "+windowed" "-target-feature" "+sext" "-target-feature" "+nsa" +// MCPU-ESP32S2: "-target-feature" "+mul32" "-target-feature" "+mul32high" "-target-feature" "+div32" "-target-feature" "+threadptr" +// MCPU-ESP32S2: "-target-feature" "+memctl" "-target-feature" "+debug" "-target-feature" "+exception" "-target-feature" "+highpriinterrupts" +// MCPU-ESP32S2: "-target-feature" "+coprocessor" "-target-feature" "+interrupt" "-target-feature" "+rvector" "-target-feature" "+timerint" +// MCPU-ESP32S2: "-target-feature" "+prid" "-target-feature" "+regprotect" "-target-feature" "+miscsr" "-target-feature" "+esp32s2" + +// RUN: %clang -target xtensa -### -c %s 2>&1 -mcpu=esp32s3 | FileCheck -check-prefix=MCPU-ESP32S3 %s +// MCPU-ESP32S3: "-target-cpu" "esp32s3" +// MCPU-ESP32S3: "-target-feature" "+density" "-target-feature" "+fp" "-target-feature" "+windowed" "-target-feature" "+bool" +// MCPU-ESP32S3: "-target-feature" "+loop" "-target-feature" "+sext" "-target-feature" "+nsa" "-target-feature" "+mul32" +// MCPU-ESP32S3: "-target-feature" "+mul32high" "-target-feature" "+div32" "-target-feature" "+mac16" "-target-feature" "+dfpaccel" +// MCPU-ESP32S3: "-target-feature" "+s32c1i" "-target-feature" "+threadptr" "-target-feature" "+atomctl" "-target-feature" "+memctl" +// MCPU-ESP32S3: "-target-feature" "+debug" "-target-feature" "+exception" "-target-feature" "+highpriinterrupts" +// MCPU-ESP32S3: "-target-feature" "+coprocessor" "-target-feature" "+interrupt" "-target-feature" "+rvector" "-target-feature" "+timerint" +// MCPU-ESP32S3: "-target-feature" "+prid" "-target-feature" "+regprotect" "-target-feature" "+miscsr" "-target-feature" "+esp32s3" diff --git a/clang/test/Driver/xtensa-toolchain.c b/clang/test/Driver/xtensa-toolchain.c new file mode 100644 index 0000000000000..7cf4f151de2fc --- /dev/null +++ b/clang/test/Driver/xtensa-toolchain.c @@ -0,0 +1,125 @@ +// A basic clang -cc1 command-line, and simple environment check. + +// RUN: %clang %s -### -no-canonical-prefixes -target xtensa-esp-elf \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree 2>&1 \ +// RUN: | FileCheck -check-prefix=CC1-ESP-DEFAULT %s +// CC1-ESP-DEFAULT: clang{{.*}} "-cc1" "-triple" "xtensa-esp-unknown-elf" + +// RUN: %clang %s -### -no-canonical-prefixes -target xtensa-esp-elf -mcpu=esp32\ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree 2>&1 \ +// RUN: | FileCheck -check-prefix=CC1-ESP32 %s +// CC1-ESP32: clang{{.*}} "-cc1" "-triple" "xtensa-esp-unknown-elf" {{.*}}"-target-cpu" "esp32" + +// RUN: %clang %s -### -no-canonical-prefixes -target xtensa-esp-elf -mcpu=esp32s2\ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree 2>&1 \ +// RUN: | FileCheck -check-prefix=CC1-ESP32S2 %s +// CC1-ESP32S2: clang{{.*}} "-cc1" "-triple" "xtensa-esp-unknown-elf" {{.*}}"-target-cpu" "esp32s2" + +// RUN: %clang %s -### -no-canonical-prefixes -target xtensa-esp-elf -mcpu=esp32s3\ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree 2>&1 \ +// RUN: | FileCheck -check-prefix=CC1-ESP32S3 %s +// CC1-ESP32S3: clang{{.*}} "-cc1" "-triple" "xtensa-esp-unknown-elf" {{.*}}"-target-cpu" "esp32s3" + +// RUN: %clang %s -### -no-canonical-prefixes -fuse-ld= \ +// RUN: -target xtensa-esp-elf --rtlib=platform \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree 2>&1 \ +// RUN: | FileCheck -check-prefix=C-XTENSA-ESP32-BAREMETAL %s + +// C-XTENSA-ESP32-BAREMETAL: "{{.*}}Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}xtensa-esp32-elf-ld" +// C-XTENSA-ESP32-BAREMETAL: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}lib{{/|\\\\}}gcc{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}8.4.0/no-rtti" +// C-XTENSA-ESP32-BAREMETAL: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}lib/no-rtti" + +// RUN: %clang %s -### -no-canonical-prefixes -fuse-ld= \ +// RUN: -target xtensa-esp-elf --rtlib=platform \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree -frtti 2>&1 \ +// RUN: | FileCheck -check-prefix=C-XTENSA-ESP32-BAREMETAL-RTTI %s + +// C-XTENSA-ESP32-BAREMETAL-RTTI: "{{.*}}Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}xtensa-esp32-elf-ld" +// C-XTENSA-ESP32-BAREMETAL-RTTI: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}lib{{/|\\\\}}gcc{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}8.4.0" +// C-XTENSA-ESP32-BAREMETAL-RTTI: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}lib" + +// RUN: %clang %s -### -no-canonical-prefixes -fuse-ld= \ +// RUN: -target xtensa-esp-elf --rtlib=platform \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree -mfix-esp32-psram-cache-issue 2>&1 \ +// RUN: | FileCheck -check-prefix=C-XTENSA-ESP32-BAREMETAL-PSRAM %s + +// C-XTENSA-ESP32-BAREMETAL-PSRAM: "{{.*}}Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}xtensa-esp32-elf-ld" +// C-XTENSA-ESP32-BAREMETAL-PSRAM: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}lib{{/|\\\\}}gcc{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}8.4.0/esp32-psram/no-rtti" +// C-XTENSA-ESP32-BAREMETAL-PSRAM: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}lib/esp32-psram/no-rtti" + +// RUN: %clang %s -### -no-canonical-prefixes -fuse-ld= \ +// RUN: -target xtensa-esp-elf --rtlib=platform \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree -mfix-esp32-psram-cache-issue -frtti 2>&1 \ +// RUN: | FileCheck -check-prefix=C-XTENSA-ESP32-BAREMETAL-PSRAM-RTTI %s + +// C-XTENSA-ESP32-BAREMETAL-PSRAM-RTTI: "{{.*}}Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}xtensa-esp32-elf-ld" +// C-XTENSA-ESP32-BAREMETAL-PSRAM-RTTI: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}lib{{/|\\\\}}gcc{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}8.4.0/esp32-psram" +// C-XTENSA-ESP32-BAREMETAL-PSRAM-RTTI: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}lib/esp32-psram" + +// RUN: %clang %s -### -no-canonical-prefixes -fuse-ld= \ +// RUN: -target xtensa-esp-elf -mcpu=esp32s2 --rtlib=platform \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree 2>&1 \ +// RUN: | FileCheck -check-prefix=C-XTENSA-ESP32S2-BAREMETAL %s + +// C-XTENSA-ESP32S2-BAREMETAL: "{{.*}}Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}xtensa-esp32s2-elf-ld" +// C-XTENSA-ESP32S2-BAREMETAL: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}lib{{/|\\\\}}gcc{{/|\\\\}}xtensa-esp32s2-elf{{/|\\\\}}8.4.0/no-rtti" +// C-XTENSA-ESP32S2-BAREMETAL: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}xtensa-esp32s2-elf{{/|\\\\}}lib/no-rtti" + +// RUN: %clang %s -### -no-canonical-prefixes -fuse-ld= \ +// RUN: -target xtensa-esp-elf -mcpu=esp32s2 --rtlib=platform \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree -frtti 2>&1 \ +// RUN: | FileCheck -check-prefix=C-XTENSA-ESP32S2-BAREMETAL-RTTI %s + +// C-XTENSA-ESP32S2-BAREMETAL-RTTI: "{{.*}}Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}xtensa-esp32s2-elf-ld" +// C-XTENSA-ESP32S2-BAREMETAL-RTTI: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}lib{{/|\\\\}}gcc{{/|\\\\}}xtensa-esp32s2-elf{{/|\\\\}}8.4.0" +// C-XTENSA-ESP32S2-BAREMETAL-RTTI: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s2-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}xtensa-esp32s2-elf{{/|\\\\}}lib" + +// RUN: %clang %s -### -no-canonical-prefixes -fuse-ld= \ +// RUN: -target xtensa-esp-elf -mcpu=esp32s3 --rtlib=platform \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree 2>&1 \ +// RUN: | FileCheck -check-prefix=C-XTENSA-ESP32S3-BAREMETAL %s + +// C-XTENSA-ESP32S3-BAREMETAL: "{{.*}}Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}xtensa-esp32s3-elf-ld" +// C-XTENSA-ESP32S3-BAREMETAL: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}lib{{/|\\\\}}gcc{{/|\\\\}}xtensa-esp32s3-elf{{/|\\\\}}8.4.0/no-rtti" +// C-XTENSA-ESP32S3-BAREMETAL: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}xtensa-esp32s3-elf{{/|\\\\}}lib/no-rtti" + +// RUN: %clang %s -### -no-canonical-prefixes -fuse-ld= \ +// RUN: -target xtensa-esp-elf -mcpu=esp32s3 --rtlib=platform \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree -frtti 2>&1 \ +// RUN: | FileCheck -check-prefix=C-XTENSA-ESP32S3-BAREMETAL-RTTI %s + +// C-XTENSA-ESP32S3-BAREMETAL-RTTI: "{{.*}}Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}xtensa-esp32s3-elf-ld" +// C-XTENSA-ESP32S3-BAREMETAL-RTTI: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}lib{{/|\\\\}}gcc{{/|\\\\}}xtensa-esp32s3-elf{{/|\\\\}}8.4.0" +// C-XTENSA-ESP32S3-BAREMETAL-RTTI: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32s3-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}xtensa-esp32s3-elf{{/|\\\\}}lib" + +// RUN: %clang %s -### -no-canonical-prefixes \ +// RUN: -target xtensa-esp-elf -mcpu=esp32 --rtlib=platform \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree \ +// RUN: --sysroot=%S/Inputs/multilib_xtensa_tree/xtensa-esp32-elf 2>&1 \ +// RUN: | FileCheck -check-prefix=C-XTENSA-ESP32-SYSROOT-BAREMETAL %s + +// C-XTENSA-ESP32-SYSROOT-BAREMETAL: "{{.*}}Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}xtensa-esp32-elf-ld" +// C-XTENSA-ESP32-SYSROOT-BAREMETAL: "--sysroot={{.*}}/Inputs/multilib_xtensa_tree/xtensa-esp32-elf" +// C-XTENSA-ESP32-SYSROOT-BAREMETAL: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}lib{{/|\\\\}}gcc{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}8.4.0/no-rtti" +// C-XTENSA-ESP32-SYSROOT-BAREMETAL: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}lib/no-rtti" + +// RUN: %clang++ %s -### -no-canonical-prefixes \ +// RUN: -target xtensa-esp-elf -mcpu=esp32 -stdlib=libstdc++ --rtlib=platform \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree 2>&1 \ +// RUN: | FileCheck -check-prefix=CXX-XTENSA-ESP32-BAREMETAL %s + +// CXX-XTENSA-ESP32-BAREMETAL: "-internal-isystem" "{{.*}}Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}xtensa-esp32-elf/include/c++{{/|\\\\}}8.4.0" +// CXX-XTENSA-ESP32-BAREMETAL: "{{.*}}Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}xtensa-esp32-elf-ld" +// CXX-XTENSA-ESP32-BAREMETAL: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}lib{{/|\\\\}}gcc{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}8.4.0/no-rtti" +// CXX-XTENSA-ESP32-BAREMETAL: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}lib/no-rtti" + +// RUN: %clang++ %s -### -no-canonical-prefixes \ +// RUN: -target xtensa-esp-elf -mcpu=esp32 -stdlib=libstdc++ --rtlib=platform \ +// RUN: --gcc-toolchain=%S/Inputs/multilib_xtensa_tree \ +// RUN: --sysroot=%S/Inputs/multilib_xtensa_tree/xtensa-esp32-elf 2>&1 \ +// RUN: | FileCheck -check-prefix=CXX-XTENSA-ESP32-SYSROOT-BAREMETAL %s + +// CXX-XTENSA-ESP32-SYSROOT-BAREMETAL: "-internal-isystem" "{{.*}}Inputs/multilib_xtensa_tree/xtensa-esp32-elf/include/c++/8.4.0" +// CXX-XTENSA-ESP32-SYSROOT-BAREMETAL: "{{.*}}Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}bin{{/|\\\\}}xtensa-esp32-elf-ld" +// CXX-XTENSA-ESP32-SYSROOT-BAREMETAL: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}lib{{/|\\\\}}gcc{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}8.4.0/no-rtti" +// CXX-XTENSA-ESP32-SYSROOT-BAREMETAL: "-L{{.*}}/Inputs/multilib_xtensa_tree/lib/gcc/xtensa-esp32-elf/8.4.0/../../..{{/|\\\\}}..{{/|\\\\}}xtensa-esp32-elf{{/|\\\\}}lib/no-rtti" diff --git a/clang/test/Misc/target-invalid-cpu-note.c b/clang/test/Misc/target-invalid-cpu-note.c index 95e24b840145b..67b0cfb195e3e 100644 --- a/clang/test/Misc/target-invalid-cpu-note.c +++ b/clang/test/Misc/target-invalid-cpu-note.c @@ -94,3 +94,7 @@ // RUN: not %clang_cc1 -triple riscv64 -tune-cpu not-a-cpu -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix TUNE-RISCV64 // TUNE-RISCV64: error: unknown target CPU 'not-a-cpu' // TUNE-RISCV64-NEXT: note: valid target CPU values are: generic-rv64, rocket-rv64, sifive-7-rv64, sifive-s21, sifive-s51, sifive-s54, sifive-s76, sifive-u54, sifive-u74, generic, rocket, sifive-7-series{{$}} + +// RUN: not %clang_cc1 -triple xtensa -tune-cpu not-a-cpu -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix TUNE-XTENSA +// TUNE-XTENSA: error: unknown target CPU 'not-a-cpu' +// TUNE-XTENSA: note: valid target CPU values are: generic, esp8266, esp32, esp32s2, esp32-s2, esp32s3, esp32-s3 diff --git a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake index a1da35b0ac4b5..43bd0fe449afb 100644 --- a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake +++ b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake @@ -16,6 +16,7 @@ set(SPARCV9 sparcv9) set(WASM32 wasm32) set(WASM64 wasm64) set(VE ve) +set(XTENSA xtensa) if(APPLE) set(ARM64 arm64) @@ -25,7 +26,7 @@ endif() set(ALL_SANITIZER_COMMON_SUPPORTED_ARCH ${X86} ${X86_64} ${PPC64} ${RISCV64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64} ${S390X} ${SPARC} ${SPARCV9} - ${HEXAGON} ${LOONGARCH64}) + ${HEXAGON} ${XTENSA} ${LOONGARCH64}) set(ALL_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64} ${MIPS32} ${MIPS64} ${PPC64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON} ${LOONGARCH64}) diff --git a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake index 9b5e03a6607ba..325307c6f6621 100644 --- a/compiler-rt/cmake/Modules/CompilerRTUtils.cmake +++ b/compiler-rt/cmake/Modules/CompilerRTUtils.cmake @@ -205,6 +205,8 @@ macro(detect_target_arch) add_default_target_arch(sparc) elseif(__WEBASSEMBLY32) add_default_target_arch(wasm32) + elseif(__XTENSA) + add_default_target_arch(xtensa) elseif(__WEBASSEMBLY64) add_default_target_arch(wasm64) elseif(__VE) diff --git a/compiler-rt/cmake/base-config-ix.cmake b/compiler-rt/cmake/base-config-ix.cmake index 8a6219568b3f4..4d36a435d7f7e 100644 --- a/compiler-rt/cmake/base-config-ix.cmake +++ b/compiler-rt/cmake/base-config-ix.cmake @@ -246,6 +246,8 @@ macro(test_targets) test_target_arch(riscv64 "" "") elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "wasm32") test_target_arch(wasm32 "" "--target=wasm32-unknown-unknown") + elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "xtensa") + test_target_arch(xtensa "" "--target=xtensa") elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "wasm64") test_target_arch(wasm64 "" "--target=wasm64-unknown-unknown") elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "ve") diff --git a/compiler-rt/cmake/builtin-config-ix.cmake b/compiler-rt/cmake/builtin-config-ix.cmake index 439abc713bad9..0c40e71d49333 100644 --- a/compiler-rt/cmake/builtin-config-ix.cmake +++ b/compiler-rt/cmake/builtin-config-ix.cmake @@ -60,6 +60,7 @@ set(SPARC sparc) set(SPARCV9 sparcv9) set(WASM32 wasm32) set(WASM64 wasm64) +set(XTENSA xtensa) set(VE ve) if(APPLE) @@ -72,7 +73,7 @@ set(ALL_BUILTIN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${AVR} ${HEXAGON} ${MIPS32} ${MIPS64} ${PPC32} ${PPC64} ${RISCV32} ${RISCV64} ${SPARC} ${SPARCV9} - ${WASM32} ${WASM64} ${VE}) + ${WASM32} ${WASM64} ${VE} ${XTENSA}) include(CompilerRTUtils) include(CompilerRTDarwinUtils) diff --git a/compiler-rt/cmake/crt-config-ix.cmake b/compiler-rt/cmake/crt-config-ix.cmake index 78d1a0de1c8a7..be90c090ba726 100644 --- a/compiler-rt/cmake/crt-config-ix.cmake +++ b/compiler-rt/cmake/crt-config-ix.cmake @@ -28,9 +28,10 @@ set(PPC64 powerpc64 powerpc64le) set(RISCV32 riscv32) set(RISCV64 riscv64) set(VE ve) +set(XTENSA xtensa) set(ALL_CRT_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC32} - ${PPC64} ${RISCV32} ${RISCV64} ${VE} ${HEXAGON}) + ${PPC64} ${RISCV32} ${RISCV64} ${VE} ${HEXAGON} ${XTENSA}) include(CompilerRTUtils) diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt index ec668e294d6d7..f4fcbcd4e5713 100644 --- a/compiler-rt/lib/builtins/CMakeLists.txt +++ b/compiler-rt/lib/builtins/CMakeLists.txt @@ -674,6 +674,8 @@ set(riscv64_SOURCES set(sparc_SOURCES ${GENERIC_SOURCES} ${GENERIC_TF_SOURCES}) set(sparcv9_SOURCES ${GENERIC_SOURCES} ${GENERIC_TF_SOURCES}) +set(xtensa_SOURCES ${GENERIC_SOURCES} ${GENERIC_TF_SOURCES}) + set(wasm32_SOURCES ${GENERIC_TF_SOURCES} ${GENERIC_SOURCES} diff --git a/compiler-rt/lib/crt/crtbegin.c b/compiler-rt/lib/crt/crtbegin.c index 7b041ff00b6b6..20de68c4620c3 100644 --- a/compiler-rt/lib/crt/crtbegin.c +++ b/compiler-rt/lib/crt/crtbegin.c @@ -69,6 +69,13 @@ __asm__(".pushsection .init,\"ax\",@progbits\n\t" __asm__(".pushsection .init,\"ax\",@progbits\n\t" "call " __USER_LABEL_PREFIX__ "__do_init\n\t" ".popsection"); +#elif defined(__xtensa__) +__asm__(".pushsection .init.literal,\"ax\",@progbits\n\t" + ".popsection\n\t" + ".pushsection .init,\"ax\",@progbits\n\t" + "movi a8, __do_init\n\t" + "callx8 a8\n\t" + ".popsection"); #else #error "crtbegin without .init_fini array unimplemented for this architecture" #endif // CRT_HAS_INITFINI_ARRAY @@ -122,6 +129,13 @@ __asm__(".pushsection .fini,\"ax\",@progbits\n\t" __asm__(".pushsection .fini,\"ax\",@progbits\n\t" "call " __USER_LABEL_PREFIX__ "__do_fini\n\t" ".popsection"); +#elif defined(__xtensa__) +__asm__(".pushsection .fini.literal,\"ax\",@progbits\n\t" + ".popsection\n\t" + ".pushsection .fini,\"ax\",@progbits\n\t" + "movi a8, __do_fini\n\t" + "callx8 a8\n\t" + ".popsection"); #else #error "crtbegin without .init_fini array unimplemented for this architecture" #endif // CRT_HAS_INIT_FINI_ARRAY diff --git a/lld/ELF/Arch/Xtensa.cpp b/lld/ELF/Arch/Xtensa.cpp new file mode 100644 index 0000000000000..10d6c3b52d1dc --- /dev/null +++ b/lld/ELF/Arch/Xtensa.cpp @@ -0,0 +1,176 @@ +//===- Xtensa.cpp ---------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "Target.h" +#include +#include +#include + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +namespace { + +class Xtensa final : public TargetInfo { +public: + Xtensa(); + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; +}; + +} // namespace + +Xtensa::Xtensa() {} + +RelExpr Xtensa::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_XTENSA_32: + return R_ABS; + case R_XTENSA_SLOT0_OP: + // This relocation is used for various instructions, with varying ways to + // calculate the relocation value. This is unlike most ELF architectures, + // and is arguably bad design (see the comment on R_386_GOT32 in X86.cpp). + // But that's what compilers emit, so it needs to be supported. + // + // We work around this by returning R_PC here and calculating the PC address + // in Xtensa::relocate based on the relative value. That's ugly. A better + // solution would be to look at the instruction here and emit various + // Xtensa-specific RelTypes, but that has another problem: the RelExpr enum + // is at its maximum size of 64. This will need to be fixed eventually, but + // for now hack around it and return R_PC. + return R_PC; + case R_XTENSA_ASM_EXPAND: + // This relocation appears to be emitted by the GNU Xtensa compiler as a + // linker relaxation hint. For example, for the following code: + // + // .section .foo + // .align 4 + // foo: + // nop + // nop + // call0 bar + // .align 4 + // bar: + // + // The call0 instruction is compiled to a l32r and callx0 instruction. + // The LLVM Xtensa backend does not emit this relocation. + // Because it's a relaxation hint, this relocation can be ignored for now + // until linker relaxations are implemented. + return R_NONE; + case R_XTENSA_DIFF8: + case R_XTENSA_DIFF16: + case R_XTENSA_DIFF32: + case R_XTENSA_PDIFF8: + case R_XTENSA_PDIFF16: + case R_XTENSA_PDIFF32: + case R_XTENSA_NDIFF8: + case R_XTENSA_NDIFF16: + case R_XTENSA_NDIFF32: + // > Xtensa relocations to mark the difference of two local symbols. + // > These are only needed to support linker relaxation and can be ignored + // > when not relaxing. + // Source: + // https://github.com/espressif/binutils-gdb/commit/30ce8e47fad9b057b6d7af9e1d43061126d34d20: + // Because we don't do linker relaxation, we can ignore these relocations. + return R_NONE; + default: + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; + } +} + +static inline bool isRRI8Branch(uint8_t *loc) { + if ((loc[0] & 0x0f) == 0b0111) { + // instructions: ball, bany, bbc, bbci, bbs, bbsi, beq, bge, bgeu, blt, + // bltu, bnall, bne, bnone + return true; + } + if ((loc[0] & 0b11'1111) == 0b10'0110) { + // instructions: beqi, bgei, bnei, blti + return true; + } + if ((loc[0] & 0b1011'1111) == 0b1011'0110) { + // instructions: bgeui, bltui + return true; + } + // some other instruction + return false; +} + +void Xtensa::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.type) { + case R_XTENSA_32: + write32le(loc, val); + break; + case R_XTENSA_SLOT0_OP: { + // HACK: calculate the instruction location based on the PC-relative + // relocation value. + uint64_t dest = rel.sym->getVA(rel.addend); + uint64_t p = dest - val; + + // This relocation is used for various instructions. + // Look at the instruction to determine how to do the relocation. + uint8_t opcode = loc[0] & 0x0f; + if (opcode == 0b0001) { // RI16 format: l32r + uint64_t val = dest - ((p + 3) & (uint64_t)0xfffffffc); + checkInt(loc, static_cast(val) >> 2, 16, rel); + checkAlignment(loc, val, 4, rel); + write16le(loc + 1, static_cast(val) >> 2); + } else if (opcode == 0b0101) { // call0, call4, call8, call12 (CALL format) + uint64_t val = dest - ((p + 4) & (uint64_t)0xfffffffc); + checkInt(loc, static_cast(val) >> 2, 18, rel); + checkAlignment(loc, val, 4, rel); + const int64_t target = static_cast(val) >> 2; + loc[0] = (loc[0] & 0b0011'1111) | ((target & 0b0000'0011) << 6); + loc[1] = target >> 2; + loc[2] = target >> 10; + } else if ((loc[0] & 0x3f) == 0b00'0110) { // j (CALL format) + uint64_t valJ = val - 4; + checkInt(loc, static_cast(valJ), 18, rel); + loc[0] = (loc[0] & 0b0011'1111) | ((valJ & 0b0000'0011) << 6); + loc[1] = valJ >> 2; + loc[2] = valJ >> 10; + } else if (isRRI8Branch(loc)) { // RRI8 format (various branch instructions) + uint64_t v = val - 4; + checkInt(loc, static_cast(v), 8, rel); + loc[2] = v & 0xff; + } else if ((loc[0] & 0b1000'1111) == 0b1000'1100) { // RI16 format: beqz.n, bnez.n + uint64_t v = val - 4; + checkUInt(loc, v, 6, rel); + loc[0] = (loc[0] & 0xcf) | (v & 0x30); + loc[1] = (loc[1] & 0x0f) | ((v & 0x0f) << 4); + } else if ((loc[0] & 0b0011'1111) == 0b0001'0110) { // BRI12 format: beqz, bgez, bltz, bnez + uint64_t v = val - 4; + checkInt(loc, static_cast(v), 12, rel); + loc[1] = ((loc[1] & 0x0f)) | ((v & 0x0f) << 4); + loc[2] = (v >> 4) & 0xff; + } else { + error(getErrorLocation(loc) + + "unknown opcode for relocation: " + std::to_string(loc[0])); + } + break; + } + default: + llvm_unreachable("unknown relocation"); + } +} + +TargetInfo *elf::getXtensaTargetInfo() { + static Xtensa target; + return ⌖ +} diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt index b37035d3e7429..44498e296c73e 100644 --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -22,6 +22,7 @@ add_lld_library(lldELF Arch/SPARCV9.cpp Arch/X86.cpp Arch/X86_64.cpp + Arch/Xtensa.cpp ARMErrataFix.cpp CallGraphSort.cpp DWARF.cpp diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 927dc272b5326..5e8a0b6d3c2ac 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1538,6 +1538,8 @@ static uint16_t getBitcodeMachineKind(StringRef path, const Triple &t) { return t.isOSIAMCU() ? EM_IAMCU : EM_386; case Triple::x86_64: return EM_X86_64; + case Triple::xtensa: + return EM_XTENSA; default: error(path + ": could not infer e_machine from bitcode target triple " + t.str()); diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index 7bc5121eabe4a..b7f05930ce800 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -87,6 +87,8 @@ TargetInfo *elf::getTarget() { return getSPARCV9TargetInfo(); case EM_X86_64: return getX86_64TargetInfo(); + case EM_XTENSA: + return getXtensaTargetInfo(); } llvm_unreachable("unknown target machine"); } diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index 14b1f53c6a819..347c676bd7def 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -187,6 +187,7 @@ TargetInfo *getRISCVTargetInfo(); TargetInfo *getSPARCV9TargetInfo(); TargetInfo *getX86TargetInfo(); TargetInfo *getX86_64TargetInfo(); +TargetInfo *getXtensaTargetInfo(); template TargetInfo *getMipsTargetInfo(); struct ErrorPlace { diff --git a/lld/test/ELF/xtensa-reloc.s b/lld/test/ELF/xtensa-reloc.s new file mode 100644 index 0000000000000..e14151ae4a814 --- /dev/null +++ b/lld/test/ELF/xtensa-reloc.s @@ -0,0 +1,57 @@ +# REQUIRES: xtensa +# RUN: llvm-mc -filetype=obj -triple=xtensa -mcpu=esp32 %s -o %t.o +# RUN: ld.lld %t.o --defsym=a=0x2000 --section-start=.CALL=0x1000 --defsym=b=0x40 --defsym=c=0x140 --section-start=.BRANCH=0x5000 --defsym=d=0x5010 --section-start=.BR12=0x100 -o %t +# RUN: llvm-objdump -d --print-imm-hex %t | FileCheck %s + +.section .BR12,"ax",@progbits +# CHECK-LABEL:section .BR12 +# CHECK: beqz a2, . +64 +# CHECK-NEXT: bnez a3, . +61 +# CHECK-NEXT: bgez a4, . +58 +# CHECK-NEXT: bltz a5, . +55 + beqz a2, c + bnez a3, c + bgez a4, c + bltz a5, c + +.section .CALL,"ax",@progbits +# CHECK-LABEL: section .CALL: +# CHECK: call0 . +4096 +# CHECK-NEXT: call0 . +4096 +# CHECK-NEXT: call0 . +4092 +# CHECK-NEXT: call0 . +4088 +# CHECK-NEXT: j . +4084 +# CHECK-NEXT: j . +4081 +# CHECK-NEXT: j . +4078 +# CHECK-NEXT: j . -4053 +# CHECK-NEXT: j . -3800 +# CHECK-NEXT: call0 . -4056 +# CHECK-NEXT: call0 . -3804 +# CHECK-NEXT: l32r a3, . -4065 +# CHECK-NEXT: callx0 a3 +# CHECK-NEXT: l32r a4, . -3815 +# CHECK-NEXT: callx0 a4 + call0 a + call0 a + call0 a + call0 a + j a + j a + j a + j b + j c + call0 b + call0 c + l32r a3, b + callx0 a3 + l32r a4, c + callx0 a4 + +.section .BRANCH,"ax",@progbits +# CHECK-LABEL: section .BRANCH: +# CHECK: beq a3, a4, . +16 +# CHECK-NEXT: ball a3, a4, . +13 +# CHECK-NEXT: blt a3, a4, . +10 + beq a3, a4, d + ball a3, a4, d + blt a3, a4, d diff --git a/lld/test/lit.cfg.py b/lld/test/lit.cfg.py index 96a1d652573fe..45c9e014a649d 100644 --- a/lld/test/lit.cfg.py +++ b/lld/test/lit.cfg.py @@ -74,7 +74,8 @@ 'RISCV': 'riscv', 'Sparc': 'sparc', 'WebAssembly': 'wasm', - 'X86': 'x86'}), + 'X86': 'x86', + 'Xtensa': 'xtensa'}), ('--assertion-mode', {'ON': 'asserts'}), ]) diff --git a/llvm/include/llvm/ADT/Triple.h b/llvm/include/llvm/ADT/Triple.h index ba4584dc60faf..67085cb99c533 100644 --- a/llvm/include/llvm/ADT/Triple.h +++ b/llvm/include/llvm/ADT/Triple.h @@ -85,6 +85,7 @@ class Triple { x86, // X86: i[3-9]86 x86_64, // X86-64: amd64, x86_64 xcore, // XCore: xcore + xtensa, // Tensilica Xtensa nvptx, // NVPTX: 32-bit nvptx64, // NVPTX: 64-bit le32, // le32: generic little-endian 32-bit CPU (PNaCl) @@ -176,6 +177,7 @@ class Triple { Mesa, SUSE, OpenEmbedded, + Espressif, LastVendorType = OpenEmbedded }; enum OSType { diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 99e7a9868c296..aca8d5ccd23c4 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -925,6 +925,21 @@ enum { #include "ELFRelocs/LoongArch.def" }; +// Xtensa specific e_flags +enum : unsigned { + // Four-bit Xtensa machine type mask. + EF_XTENSA_MACH = 0x0000000f, + // Various CPU types. + EF_XTENSA_MACH_NONE = 0x00000000, // A base Xtensa implementation + EF_XTENSA_XT_INSN = 0x00000100, + EF_XTENSA_XT_LIT = 0x00000200, +}; + +// ELF Relocation types for Xtensa +enum { +#include "ELFRelocs/Xtensa.def" +}; + #undef ELF_RELOC // Section header. diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/Xtensa.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/Xtensa.def new file mode 100644 index 0000000000000..204e20f54301f --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/Xtensa.def @@ -0,0 +1,65 @@ +#ifndef ELF_RELOC +#error "ELF_RELOC must be defined" +#endif + +ELF_RELOC(R_XTENSA_NONE, 0) +ELF_RELOC(R_XTENSA_32, 1) +ELF_RELOC(R_XTENSA_RTLD, 2) +ELF_RELOC(R_XTENSA_GLOB_DAT, 3) +ELF_RELOC(R_XTENSA_JMP_SLOT, 4) +ELF_RELOC(R_XTENSA_RELATIVE, 5) +ELF_RELOC(R_XTENSA_PLT, 6) +ELF_RELOC(R_XTENSA_OP0, 8) +ELF_RELOC(R_XTENSA_OP1, 9) +ELF_RELOC(R_XTENSA_OP2, 10) +ELF_RELOC(R_XTENSA_ASM_EXPAND, 11) +ELF_RELOC(R_XTENSA_ASM_SIMPLIFY, 12) +ELF_RELOC(R_XTENSA_32_PCREL, 14) +ELF_RELOC(R_XTENSA_GNU_VTINHERIT, 15) +ELF_RELOC(R_XTENSA_GNU_VTENTRY, 16) +ELF_RELOC(R_XTENSA_DIFF8, 17) +ELF_RELOC(R_XTENSA_DIFF16, 18) +ELF_RELOC(R_XTENSA_DIFF32, 19) +ELF_RELOC(R_XTENSA_SLOT0_OP, 20) +ELF_RELOC(R_XTENSA_SLOT1_OP, 21) +ELF_RELOC(R_XTENSA_SLOT2_OP, 22) +ELF_RELOC(R_XTENSA_SLOT3_OP, 23) +ELF_RELOC(R_XTENSA_SLOT4_OP, 24) +ELF_RELOC(R_XTENSA_SLOT5_OP, 25) +ELF_RELOC(R_XTENSA_SLOT6_OP, 26) +ELF_RELOC(R_XTENSA_SLOT7_OP, 27) +ELF_RELOC(R_XTENSA_SLOT8_OP, 28) +ELF_RELOC(R_XTENSA_SLOT9_OP, 29) +ELF_RELOC(R_XTENSA_SLOT10_OP, 30) +ELF_RELOC(R_XTENSA_SLOT11_OP, 31) +ELF_RELOC(R_XTENSA_SLOT12_OP, 32) +ELF_RELOC(R_XTENSA_SLOT13_OP, 33) +ELF_RELOC(R_XTENSA_SLOT14_OP, 34) +ELF_RELOC(R_XTENSA_SLOT0_ALT, 35) +ELF_RELOC(R_XTENSA_SLOT1_ALT, 36) +ELF_RELOC(R_XTENSA_SLOT2_ALT, 37) +ELF_RELOC(R_XTENSA_SLOT3_ALT, 38) +ELF_RELOC(R_XTENSA_SLOT4_ALT, 39) +ELF_RELOC(R_XTENSA_SLOT5_ALT, 40) +ELF_RELOC(R_XTENSA_SLOT6_ALT, 41) +ELF_RELOC(R_XTENSA_SLOT7_ALT, 42) +ELF_RELOC(R_XTENSA_SLOT8_ALT, 43) +ELF_RELOC(R_XTENSA_SLOT9_ALT, 44) +ELF_RELOC(R_XTENSA_SLOT10_ALT, 45) +ELF_RELOC(R_XTENSA_SLOT11_ALT, 46) +ELF_RELOC(R_XTENSA_SLOT12_ALT, 47) +ELF_RELOC(R_XTENSA_SLOT13_ALT, 48) +ELF_RELOC(R_XTENSA_SLOT14_ALT, 49) +ELF_RELOC(R_XTENSA_TLSDESC_FN, 50) +ELF_RELOC(R_XTENSA_TLSDESC_ARG, 51) +ELF_RELOC(R_XTENSA_TLS_DTPOFF, 52) +ELF_RELOC(R_XTENSA_TLS_TPOFF, 53) +ELF_RELOC(R_XTENSA_TLS_FUNC, 54) +ELF_RELOC(R_XTENSA_TLS_ARG, 55) +ELF_RELOC(R_XTENSA_TLS_CALL, 56) +ELF_RELOC(R_XTENSA_PDIFF8, 57) +ELF_RELOC(R_XTENSA_PDIFF16, 58) +ELF_RELOC(R_XTENSA_PDIFF32, 59) +ELF_RELOC(R_XTENSA_NDIFF8, 60) +ELF_RELOC(R_XTENSA_NDIFF16, 61) +ELF_RELOC(R_XTENSA_NDIFF32, 62) diff --git a/llvm/include/llvm/IR/CMakeLists.txt b/llvm/include/llvm/IR/CMakeLists.txt index 5151f9125b946..2ae6aa0cc23c0 100644 --- a/llvm/include/llvm/IR/CMakeLists.txt +++ b/llvm/include/llvm/IR/CMakeLists.txt @@ -20,5 +20,6 @@ tablegen(LLVM IntrinsicsS390.h -gen-intrinsic-enums -intrinsic-prefix=s390) tablegen(LLVM IntrinsicsWebAssembly.h -gen-intrinsic-enums -intrinsic-prefix=wasm) tablegen(LLVM IntrinsicsX86.h -gen-intrinsic-enums -intrinsic-prefix=x86) tablegen(LLVM IntrinsicsXCore.h -gen-intrinsic-enums -intrinsic-prefix=xcore) +tablegen(LLVM IntrinsicsXtensa.h -gen-intrinsic-enums -intrinsic-prefix=xtensa) tablegen(LLVM IntrinsicsVE.h -gen-intrinsic-enums -intrinsic-prefix=ve) add_public_tablegen_target(intrinsics_gen) diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index d46fa4fbf5b50..974674e2a286f 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -2053,3 +2053,4 @@ include "llvm/IR/IntrinsicsRISCV.td" include "llvm/IR/IntrinsicsSPIRV.td" include "llvm/IR/IntrinsicsVE.td" include "llvm/IR/IntrinsicsDirectX.td" +include "llvm/IR/IntrinsicsXtensa.td" diff --git a/llvm/include/llvm/IR/IntrinsicsXtensa.td b/llvm/include/llvm/IR/IntrinsicsXtensa.td new file mode 100644 index 0000000000000..d7d25609b5d56 --- /dev/null +++ b/llvm/include/llvm/IR/IntrinsicsXtensa.td @@ -0,0 +1,251 @@ +//===- IntrinsicsXtensa.td - Defines Xtensa intrinsics -----*- tablegen -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines all of the Xtensa-specific intrinsics. +// +//===----------------------------------------------------------------------===// + +let TargetPrefix = "xtensa" in { // All intrinsics start with "llvm.xtensa.". + +def int_xtensa_umul_aa_ll: ClangBuiltin<"__builtin_xtensa_umul_aa_ll">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; +def int_xtensa_umul_aa_hl: ClangBuiltin<"__builtin_xtensa_umul_aa_hl">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; +def int_xtensa_umul_aa_lh: ClangBuiltin<"__builtin_xtensa_umul_aa_lh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; +def int_xtensa_umul_aa_hh: ClangBuiltin<"__builtin_xtensa_umul_aa_hh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; + +def int_xtensa_mul_aa_ll: ClangBuiltin<"__builtin_xtensa_mul_aa_ll">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; +def int_xtensa_mul_aa_hl: ClangBuiltin<"__builtin_xtensa_mul_aa_hl">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; +def int_xtensa_mul_aa_lh: ClangBuiltin<"__builtin_xtensa_mul_aa_lh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; +def int_xtensa_mul_aa_hh: ClangBuiltin<"__builtin_xtensa_mul_aa_hh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; + +def int_xtensa_mul_ad_ll: ClangBuiltin<"__builtin_xtensa_mul_ad_ll">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_mul_ad_hl: ClangBuiltin<"__builtin_xtensa_mul_ad_hl">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_mul_ad_lh: ClangBuiltin<"__builtin_xtensa_mul_ad_lh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_mul_ad_hh: ClangBuiltin<"__builtin_xtensa_mul_ad_hh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; + +def int_xtensa_mul_da_ll: ClangBuiltin<"__builtin_xtensa_mul_da_ll">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_mul_da_hl: ClangBuiltin<"__builtin_xtensa_mul_da_hl">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_mul_da_lh: ClangBuiltin<"__builtin_xtensa_mul_da_lh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_mul_da_hh: ClangBuiltin<"__builtin_xtensa_mul_da_hh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; + +def int_xtensa_mul_dd_ll: ClangBuiltin<"__builtin_xtensa_mul_dd_ll">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>, ImmArg>]>; +def int_xtensa_mul_dd_hl: ClangBuiltin<"__builtin_xtensa_mul_dd_hl">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>, ImmArg>]>; +def int_xtensa_mul_dd_lh: ClangBuiltin<"__builtin_xtensa_mul_dd_lh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>, ImmArg>]>; +def int_xtensa_mul_dd_hh: ClangBuiltin<"__builtin_xtensa_mul_dd_hh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>, ImmArg>]>; + +def int_xtensa_mula_aa_ll: ClangBuiltin<"__builtin_xtensa_mula_aa_ll">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; +def int_xtensa_mula_aa_hl: ClangBuiltin<"__builtin_xtensa_mula_aa_hl">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; +def int_xtensa_mula_aa_lh: ClangBuiltin<"__builtin_xtensa_mula_aa_lh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; +def int_xtensa_mula_aa_hh: ClangBuiltin<"__builtin_xtensa_mula_aa_hh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; + +def int_xtensa_mula_ad_ll: ClangBuiltin<"__builtin_xtensa_mula_ad_ll">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_mula_ad_hl: ClangBuiltin<"__builtin_xtensa_mula_ad_hl">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_mula_ad_lh: ClangBuiltin<"__builtin_xtensa_mula_ad_lh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_mula_ad_hh: ClangBuiltin<"__builtin_xtensa_mula_ad_hh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; + +def int_xtensa_mula_da_ll: ClangBuiltin<"__builtin_xtensa_mula_da_ll">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_mula_da_hl: ClangBuiltin<"__builtin_xtensa_mula_da_hl">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_mula_da_lh: ClangBuiltin<"__builtin_xtensa_mula_da_lh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_mula_da_hh: ClangBuiltin<"__builtin_xtensa_mula_da_hh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; + +def int_xtensa_mula_dd_ll: ClangBuiltin<"__builtin_xtensa_mula_dd_ll">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>, ImmArg>]>; +def int_xtensa_mula_dd_hl: ClangBuiltin<"__builtin_xtensa_mula_dd_hl">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>, ImmArg>]>; +def int_xtensa_mula_dd_lh: ClangBuiltin<"__builtin_xtensa_mula_dd_lh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>, ImmArg>]>; +def int_xtensa_mula_dd_hh: ClangBuiltin<"__builtin_xtensa_mula_dd_hh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>, ImmArg>]>; + +def int_xtensa_muls_aa_ll: ClangBuiltin<"__builtin_xtensa_muls_aa_ll">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; +def int_xtensa_muls_aa_hl: ClangBuiltin<"__builtin_xtensa_muls_aa_hl">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; +def int_xtensa_muls_aa_lh: ClangBuiltin<"__builtin_xtensa_muls_aa_lh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; +def int_xtensa_muls_aa_hh: ClangBuiltin<"__builtin_xtensa_muls_aa_hh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; + +def int_xtensa_muls_ad_ll: ClangBuiltin<"__builtin_xtensa_muls_ad_ll">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_muls_ad_hl: ClangBuiltin<"__builtin_xtensa_muls_ad_hl">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_muls_ad_lh: ClangBuiltin<"__builtin_xtensa_muls_ad_lh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_muls_ad_hh: ClangBuiltin<"__builtin_xtensa_muls_ad_hh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; + +def int_xtensa_muls_da_ll: ClangBuiltin<"__builtin_xtensa_muls_da_ll">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_muls_da_hl: ClangBuiltin<"__builtin_xtensa_muls_da_hl">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_muls_da_lh: ClangBuiltin<"__builtin_xtensa_muls_da_lh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; +def int_xtensa_muls_da_hh: ClangBuiltin<"__builtin_xtensa_muls_da_hh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>]>; + +def int_xtensa_muls_dd_ll: ClangBuiltin<"__builtin_xtensa_muls_dd_ll">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>, ImmArg>]>; +def int_xtensa_muls_dd_hl: ClangBuiltin<"__builtin_xtensa_muls_dd_hl">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>, ImmArg>]>; +def int_xtensa_muls_dd_lh: ClangBuiltin<"__builtin_xtensa_muls_dd_lh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>, ImmArg>]>; +def int_xtensa_muls_dd_hh: ClangBuiltin<"__builtin_xtensa_muls_dd_hh">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], [ImmArg>, ImmArg>]>; + + +def int_xtensa_mula_da_ll_lddec: ClangBuiltin<"__builtin_xtensa_mula_da_ll_lddec">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>]>; +def int_xtensa_mula_da_lh_lddec: ClangBuiltin<"__builtin_xtensa_mula_da_lh_lddec">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>]>; +def int_xtensa_mula_da_hl_lddec: ClangBuiltin<"__builtin_xtensa_mula_da_hl_lddec">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>]>; +def int_xtensa_mula_da_hh_lddec: ClangBuiltin<"__builtin_xtensa_mula_da_hh_lddec">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>]>; + +def int_xtensa_mula_da_ll_ldinc: ClangBuiltin<"__builtin_xtensa_mula_da_ll_ldinc">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>]>; +def int_xtensa_mula_da_lh_ldinc: ClangBuiltin<"__builtin_xtensa_mula_da_lh_ldinc">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>]>; +def int_xtensa_mula_da_hl_ldinc: ClangBuiltin<"__builtin_xtensa_mula_da_hl_ldinc">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>]>; +def int_xtensa_mula_da_hh_ldinc: ClangBuiltin<"__builtin_xtensa_mula_da_hh_ldinc">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>]>; + +def int_xtensa_mula_dd_ll_lddec: ClangBuiltin<"__builtin_xtensa_mula_dd_ll_lddec">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>, ImmArg>]>; +def int_xtensa_mula_dd_lh_lddec: ClangBuiltin<"__builtin_xtensa_mula_dd_lh_lddec">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>, ImmArg>]>; +def int_xtensa_mula_dd_hl_lddec: ClangBuiltin<"__builtin_xtensa_mula_dd_hl_lddec">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>, ImmArg>]>; +def int_xtensa_mula_dd_hh_lddec: ClangBuiltin<"__builtin_xtensa_mula_dd_hh_lddec">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>, ImmArg>]>; + +def int_xtensa_mula_dd_ll_ldinc: ClangBuiltin<"__builtin_xtensa_mula_dd_ll_ldinc">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>, ImmArg>]>; +def int_xtensa_mula_dd_lh_ldinc: ClangBuiltin<"__builtin_xtensa_mula_dd_lh_ldinc">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>, ImmArg>]>; +def int_xtensa_mula_dd_hl_ldinc: ClangBuiltin<"__builtin_xtensa_mula_dd_hl_ldinc">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>, ImmArg>]>; +def int_xtensa_mula_dd_hh_ldinc: ClangBuiltin<"__builtin_xtensa_mula_dd_hh_ldinc">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty], + [ImmArg>, ImmArg>, ImmArg>]>; + +//===----------------------------------------------------------------------===// +// Load operations + +def int_xtensa_lddec: ClangBuiltin<"__builtin_xtensa_lddec">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty], [ImmArg>]>; + +def int_xtensa_ldinc: ClangBuiltin<"__builtin_xtensa_ldinc">, + Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty], [ImmArg>]>; + +//===----------------------------------------------------------------------===// +// WSR/XSR/RSR + +def int_xtensa_wsr_acclo: ClangBuiltin<"__builtin_xtensa_wsr_acclo">, + Intrinsic<[], [llvm_i32_ty], []>; + +def int_xtensa_rsr_acclo: ClangBuiltin<"__builtin_xtensa_rsr_acclo">, + Intrinsic<[llvm_i32_ty], [], []>; + +def int_xtensa_xsr_acclo: ClangBuiltin<"__builtin_xtensa_xsr_acclo">, + Intrinsic<[], [llvm_ptr_ty], []>; + +def int_xtensa_wsr_acchi: ClangBuiltin<"__builtin_xtensa_wsr_acchi">, + Intrinsic<[], [llvm_i32_ty], []>; + +def int_xtensa_rsr_acchi: ClangBuiltin<"__builtin_xtensa_rsr_acchi">, + Intrinsic<[llvm_i32_ty], [], []>; + +def int_xtensa_xsr_acchi: ClangBuiltin<"__builtin_xtensa_xsr_acchi">, + Intrinsic<[], [llvm_ptr_ty], []>; + +def int_xtensa_wsr_m0: ClangBuiltin<"__builtin_xtensa_wsr_m0">, + Intrinsic<[], [llvm_i32_ty], []>; + +def int_xtensa_rsr_m0: ClangBuiltin<"__builtin_xtensa_rsr_m0">, + Intrinsic<[llvm_i32_ty]>; + +def int_xtensa_xsr_m0: ClangBuiltin<"__builtin_xtensa_xsr_m0">, + Intrinsic<[], [llvm_ptr_ty], []>; + +def int_xtensa_wsr_m1: ClangBuiltin<"__builtin_xtensa_wsr_m1">, + Intrinsic<[], [llvm_i32_ty], []>; + +def int_xtensa_rsr_m1: ClangBuiltin<"__builtin_xtensa_rsr_m1">, + Intrinsic<[llvm_i32_ty], [], []>; + +def int_xtensa_xsr_m1: ClangBuiltin<"__builtin_xtensa_xsr_m1">, + Intrinsic<[], [llvm_ptr_ty], []>; + +def int_xtensa_wsr_m2: ClangBuiltin<"__builtin_xtensa_wsr_m2">, + Intrinsic<[], [llvm_i32_ty], []>; + +def int_xtensa_rsr_m2: ClangBuiltin<"__builtin_xtensa_rsr_m2">, + Intrinsic<[llvm_i32_ty], [], []>; + +def int_xtensa_xsr_m2: ClangBuiltin<"__builtin_xtensa_xsr_m2">, + Intrinsic<[], [llvm_ptr_ty], []>; + +def int_xtensa_wsr_m3: ClangBuiltin<"__builtin_xtensa_wsr_m3">, + Intrinsic<[], [llvm_i32_ty], []>; + +def int_xtensa_rsr_m3: ClangBuiltin<"__builtin_xtensa_rsr_m3">, + Intrinsic<[llvm_i32_ty], [], []>; + +def int_xtensa_xsr_m3: ClangBuiltin<"__builtin_xtensa_xsr_m3">, + Intrinsic<[], [llvm_ptr_ty], []>; + +} diff --git a/llvm/include/llvm/MC/MCSubtargetInfo.h b/llvm/include/llvm/MC/MCSubtargetInfo.h index e1f0a86141e36..1e529bf4fb31e 100644 --- a/llvm/include/llvm/MC/MCSubtargetInfo.h +++ b/llvm/include/llvm/MC/MCSubtargetInfo.h @@ -230,6 +230,14 @@ class MCSubtargetInfo { return Found != ProcDesc.end() && StringRef(Found->Key) == CPU; } + ArrayRef getCPUTable() const { + return ProcDesc; + } + + ArrayRef getFeatureTable() const { + return ProcFeatures; + } + virtual unsigned getHwMode() const { return 0; } /// Return the cache size in bytes for the given level of cache. diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h index ed2f70b0da251..0dfa00fe0e5cb 100644 --- a/llvm/include/llvm/Object/ELFObjectFile.h +++ b/llvm/include/llvm/Object/ELFObjectFile.h @@ -1213,6 +1213,8 @@ StringRef ELFObjectFile::getFileFormatName() const { return "elf32-amdgpu"; case ELF::EM_LOONGARCH: return "elf32-loongarch"; + case ELF::EM_XTENSA: + return "elf32-xtensa"; default: return "elf32-unknown"; } @@ -1337,6 +1339,9 @@ template Triple::ArchType ELFObjectFile::getArch() const { report_fatal_error("Invalid ELFCLASS!"); } + case ELF::EM_XTENSA: + return Triple::xtensa; + default: return Triple::UnknownArch; } diff --git a/llvm/include/llvm/Support/TargetParser.h b/llvm/include/llvm/Support/TargetParser.h index c3a6cceaee6be..86c1a1f34a095 100644 --- a/llvm/include/llvm/Support/TargetParser.h +++ b/llvm/include/llvm/Support/TargetParser.h @@ -192,6 +192,54 @@ bool parseBranchProtection(StringRef Spec, ParsedBranchProtection &PBP, } // namespace ARM +namespace Xtensa { + +enum CPUKind : unsigned { +#define XTENSA_CPU(ENUM, NAME, FEATURES) CK_##ENUM, +#include "XtensaTargetParser.def" +}; + +enum FeatureKind : uint64_t { + FK_INVALID = 0, + FK_NONE = 1, + FK_FP = 1 << 1, + FK_WINDOWED = 1 << 2, + FK_BOOLEAN = 1 << 3, + FK_DENSITY = 1 << 4, + FK_LOOP = 1 << 5, + FK_SEXT = 1 << 6, + FK_NSA = 1 << 7, + FK_MUL32 = 1 << 8, + FK_MUL32HIGH = 1 << 9, + FK_DIV32 = 1 << 10, + FK_MAC16 = 1 << 11, + FK_DFPACCEL = 1 << 12, + FK_S32C1I = 1 << 13, + FK_THREADPTR = 1 << 14, + FK_EXTENDEDL32R = 1 << 15, + FK_ATOMCTL = 1 << 16, + FK_MEMCTL = 1 << 17, + FK_DEBUG = 1 << 18, + FK_EXCEPTION = 1 << 19, + FK_HIGHPRIINTERRUPTS = 1 << 20, + FK_COPROCESSOR = 1 << 21, + FK_INTERRUPT = 1 << 22, + FK_RVECTOR = 1 << 23, + FK_TIMERINT = 1 << 24, + FK_PRID = 1 << 25, + FK_REGPROTECT = 1 << 26, + FK_MISCSR = 1 << 27, + FK_ESP32S2OPS = 1 << 28, + FK_ESP32S3OPS = 1 << 29 +}; + +CPUKind parseCPUKind(StringRef CPU); +StringRef getBaseName(StringRef CPU); +void getCPUFeatures(StringRef CPU, SmallVectorImpl &Features); +void fillValidCPUList(SmallVectorImpl &Values); + +} // namespace Xtensa + } // namespace llvm #endif diff --git a/llvm/include/llvm/Support/XtensaTargetParser.def b/llvm/include/llvm/Support/XtensaTargetParser.def new file mode 100644 index 0000000000000..e46020700f2e2 --- /dev/null +++ b/llvm/include/llvm/Support/XtensaTargetParser.def @@ -0,0 +1,83 @@ +//===- XtensaTargetParser.def - Xtensa target parsing defines ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides defines to build up the Xtensa target parser's logic. +// +//===----------------------------------------------------------------------===// + +#ifndef XTENSA_FEATURE +#define XTENSA_FEATURE(ID, STR) +#endif + +XTENSA_FEATURE(FK_DENSITY, "density") +XTENSA_FEATURE(FK_FP, "fp") +XTENSA_FEATURE(FK_WINDOWED, "windowed") +XTENSA_FEATURE(FK_BOOLEAN, "bool") +XTENSA_FEATURE(FK_LOOP, "loop") +XTENSA_FEATURE(FK_SEXT, "sext") +XTENSA_FEATURE(FK_NSA, "nsa") +XTENSA_FEATURE(FK_MUL32, "mul32") +XTENSA_FEATURE(FK_MUL32HIGH, "mul32high") +XTENSA_FEATURE(FK_DIV32, "div32") +XTENSA_FEATURE(FK_MAC16, "mac16") +XTENSA_FEATURE(FK_DFPACCEL, "dfpaccel") +XTENSA_FEATURE(FK_S32C1I, "s32c1i") +XTENSA_FEATURE(FK_THREADPTR, "threadptr") +XTENSA_FEATURE(FK_EXTENDEDL32R, "extendedl32r") +XTENSA_FEATURE(FK_ATOMCTL, "atomctl") +XTENSA_FEATURE(FK_MEMCTL, "memctl") +XTENSA_FEATURE(FK_DEBUG, "debug") +XTENSA_FEATURE(FK_EXCEPTION, "exception") +XTENSA_FEATURE(FK_HIGHPRIINTERRUPTS, "highpriinterrupts") +XTENSA_FEATURE(FK_COPROCESSOR, "coprocessor") +XTENSA_FEATURE(FK_INTERRUPT, "interrupt") +XTENSA_FEATURE(FK_RVECTOR, "rvector") +XTENSA_FEATURE(FK_TIMERINT, "timerint") +XTENSA_FEATURE(FK_PRID, "prid") +XTENSA_FEATURE(FK_REGPROTECT, "regprotect") +XTENSA_FEATURE(FK_MISCSR, "miscsr") +XTENSA_FEATURE(FK_ESP32S2OPS, "esp32s2") +XTENSA_FEATURE(FK_ESP32S3OPS, "esp32s3") + +#undef XTENSA_FEATURE + +#ifndef XTENSA_CPU +#define XTENSA_CPU(ENUM, NAME, FEATURES) +#endif + +XTENSA_CPU(INVALID, {"invalid"}, FK_INVALID) +XTENSA_CPU(GENERIC, {"generic"}, FK_NONE) +XTENSA_CPU(ESP8266, {"esp8266"}, + (FK_DENSITY | FK_NSA | FK_MUL32 | FK_EXTENDEDL32R | FK_DEBUG | FK_EXCEPTION | FK_HIGHPRIINTERRUPTS | + FK_INTERRUPT | FK_RVECTOR | FK_TIMERINT | FK_REGPROTECT | FK_PRID)) +XTENSA_CPU(ESP32, {"esp32"}, + (FK_DENSITY | FK_FP | FK_LOOP | FK_MAC16 | FK_WINDOWED | FK_BOOLEAN | + FK_SEXT | FK_NSA | FK_MUL32 | FK_MUL32HIGH | FK_DFPACCEL | FK_S32C1I | FK_THREADPTR | FK_DIV32 | + FK_ATOMCTL | FK_MEMCTL | FK_DEBUG | FK_EXCEPTION | FK_HIGHPRIINTERRUPTS | FK_COPROCESSOR | + FK_INTERRUPT | FK_RVECTOR | FK_TIMERINT | FK_PRID | FK_REGPROTECT | FK_MISCSR)) +XTENSA_CPU(ESP32S2, {"esp32s2"}, + (FK_DENSITY | FK_WINDOWED | FK_SEXT | FK_NSA | FK_MUL32 | FK_MUL32HIGH | FK_THREADPTR | FK_DIV32 | + FK_MEMCTL | FK_DEBUG | FK_EXCEPTION | FK_HIGHPRIINTERRUPTS | FK_COPROCESSOR | FK_INTERRUPT | + FK_RVECTOR | FK_TIMERINT | FK_PRID | FK_REGPROTECT | FK_MISCSR | FK_ESP32S2OPS)) +XTENSA_CPU(ESP32S3, {"esp32s3"}, + (FK_DENSITY | FK_FP | FK_LOOP | FK_MAC16 | FK_WINDOWED | FK_BOOLEAN | + FK_SEXT | FK_NSA | FK_MUL32 | FK_MUL32HIGH | FK_DFPACCEL | FK_S32C1I | FK_THREADPTR | FK_DIV32 | + FK_ATOMCTL | FK_MEMCTL | FK_DEBUG | FK_EXCEPTION | FK_HIGHPRIINTERRUPTS | FK_COPROCESSOR | + FK_INTERRUPT | FK_RVECTOR | FK_TIMERINT | FK_PRID | FK_REGPROTECT | FK_MISCSR | + FK_ESP32S3OPS)) + +#undef XTENSA_CPU + +#ifndef XTENSA_CPU_ALIAS +#define XTENSA_CPU_ALIAS(NAME, ALTNMAME) +#endif + +XTENSA_CPU_ALIAS("esp32s2", "esp32-s2") +XTENSA_CPU_ALIAS("esp32s3", "esp32-s3") + +#undef XTENSA_CPU_ALIAS diff --git a/llvm/include/llvm/module.modulemap b/llvm/include/llvm/module.modulemap index 76b10621541c4..3e40c71acdbd0 100644 --- a/llvm/include/llvm/module.modulemap +++ b/llvm/include/llvm/module.modulemap @@ -83,6 +83,7 @@ module LLVM_BinaryFormat { textual header "BinaryFormat/ELFRelocs/SystemZ.def" textual header "BinaryFormat/ELFRelocs/VE.def" textual header "BinaryFormat/ELFRelocs/x86_64.def" + textual header "BinaryFormat/ELFRelocs/Xtensa.def" textual header "BinaryFormat/WasmRelocs.def" textual header "BinaryFormat/MsgPack.def" } @@ -418,6 +419,7 @@ module LLVM_Utils { textual header "Support/RISCVTargetParser.def" textual header "Support/TargetOpcodes.def" textual header "Support/X86TargetParser.def" + textual header "Support/XtensaTargetParser.def" } // This part of the module is usable from both C and C++ code. diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp index d4138133721e8..fb854dc17dea8 100644 --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -48,6 +48,7 @@ #include "llvm/IR/IntrinsicsWebAssembly.h" #include "llvm/IR/IntrinsicsX86.h" #include "llvm/IR/IntrinsicsXCore.h" +#include "llvm/IR/IntrinsicsXtensa.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Metadata.h" diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp index d6fe952c0c1d8..5aff940a3a7b5 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -361,6 +361,9 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) { FDECFIEncoding = PositionIndependent ? dwarf::DW_EH_PE_pcrel : dwarf::DW_EH_PE_absptr; break; + case Triple::xtensa: + FDECFIEncoding = dwarf::DW_EH_PE_sdata4; + break; default: FDECFIEncoding = dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; break; diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp index 0d5aa91c1348e..b03f708432cc7 100644 --- a/llvm/lib/Object/ELF.cpp +++ b/llvm/lib/Object/ELF.cpp @@ -173,6 +173,13 @@ StringRef llvm::object::getELFRelocationTypeName(uint32_t Machine, break; } break; + case ELF::EM_XTENSA: + switch (Type) { +#include "llvm/BinaryFormat/ELFRelocs/Xtensa.def" + default: + break; + } + break; default: break; } diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp index 9ad2c41351672..e286c189aea00 100644 --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -535,6 +535,11 @@ void ScalarBitSetTraits::bitset(IO &IO, BCase(EF_RISCV_RVE); BCase(EF_RISCV_TSO); break; + case ELF::EM_XTENSA: + BCase(EF_XTENSA_XT_INSN); + BCaseMask(EF_XTENSA_MACH_NONE, EF_XTENSA_MACH); + BCase(EF_XTENSA_XT_LIT); + break; case ELF::EM_AMDGPU: BCaseMask(EF_AMDGPU_MACH_NONE, EF_AMDGPU_MACH); BCaseMask(EF_AMDGPU_MACH_R600_R600, EF_AMDGPU_MACH); @@ -892,6 +897,9 @@ void ScalarEnumerationTraits::enumeration( case ELF::EM_LOONGARCH: #include "llvm/BinaryFormat/ELFRelocs/LoongArch.def" break; + case ELF::EM_XTENSA: +#include "llvm/BinaryFormat/ELFRelocs/Xtensa.def" + break; default: // Nothing to do. break; diff --git a/llvm/lib/Support/TargetParser.cpp b/llvm/lib/Support/TargetParser.cpp index e5590d458fedc..80d449590f996 100644 --- a/llvm/lib/Support/TargetParser.cpp +++ b/llvm/lib/Support/TargetParser.cpp @@ -342,6 +342,81 @@ bool getCPUFeaturesExceptStdExt(CPUKind Kind, } } // namespace RISCV + +namespace Xtensa { +struct CPUInfo { + StringLiteral Name; + CPUKind Kind; + uint64_t Features; +}; + +struct FeatureName { + uint64_t ID; + const char *NameCStr; + size_t NameLength; + + StringRef getName() const { return StringRef(NameCStr, NameLength); } +}; + +const FeatureName XtensaFeatureNames[] = { +#define XTENSA_FEATURE(ID, NAME) {ID, "+" NAME, sizeof(NAME)}, +#include "llvm/Support/XtensaTargetParser.def" +}; + +constexpr CPUInfo XtensaCPUInfo[] = { +#define XTENSA_CPU(ENUM, NAME, FEATURES) {NAME, CK_##ENUM, FEATURES}, +#include "llvm/Support/XtensaTargetParser.def" +}; + +StringRef getBaseName(StringRef CPU){ + return llvm::StringSwitch(CPU) +#define XTENSA_CPU_ALIAS(NAME, ANAME) .Case(ANAME, NAME) +#include "llvm/Support/XtensaTargetParser.def" + .Default(CPU); +} + +StringRef getAliasName(StringRef CPU){ + return llvm::StringSwitch(CPU) +#define XTENSA_CPU_ALIAS(NAME, ANAME) .Case(NAME, ANAME) +#include "llvm/Support/XtensaTargetParser.def" + .Default(CPU); +} + +CPUKind parseCPUKind(StringRef CPU) { + CPU = getBaseName(CPU); + return llvm::StringSwitch(CPU) +#define XTENSA_CPU(ENUM, NAME, FEATURES) .Case(NAME, CK_##ENUM) +#include "llvm/Support/XtensaTargetParser.def" + .Default(CK_INVALID); +} + +//Get all features for the CPU +void getCPUFeatures(StringRef CPU, SmallVectorImpl &Features) { + CPU = getBaseName(CPU); + auto I = llvm::find_if(XtensaCPUInfo, + [&](const CPUInfo &CI) { return CI.Name == CPU; }); + assert(I != std::end(XtensaCPUInfo) && "CPU not found!"); + uint64_t Bits = I->Features; + + for (const auto &F : XtensaFeatureNames) { + if ((Bits & F.ID) == F.ID) + Features.push_back(F.getName()); + } +} + +//Find all valid CPUs +void fillValidCPUList(SmallVectorImpl &Values) { + for (const auto &C : XtensaCPUInfo) { + if (C.Kind != CK_INVALID) { + Values.emplace_back(C.Name); + StringRef Name = getAliasName(C.Name); + if (Name != C.Name) + Values.emplace_back(Name); + } + } +} + +} // namespace Xtensa } // namespace llvm // Parse a branch protection specification, which has the form diff --git a/llvm/lib/Support/Triple.cpp b/llvm/lib/Support/Triple.cpp index 6696d158b2c1a..ef934c18d90c6 100644 --- a/llvm/lib/Support/Triple.cpp +++ b/llvm/lib/Support/Triple.cpp @@ -83,6 +83,7 @@ StringRef Triple::getArchTypeName(ArchType Kind) { case x86: return "i386"; case x86_64: return "x86_64"; case xcore: return "xcore"; + case xtensa: return "xtensa"; } llvm_unreachable("Invalid ArchType!"); @@ -172,6 +173,8 @@ StringRef Triple::getArchTypePrefix(ArchType Kind) { case loongarch64: return "loongarch"; case dxil: return "dx"; + + case xtensa: return "xtensa"; } } @@ -193,6 +196,7 @@ StringRef Triple::getVendorTypeName(VendorType Kind) { case PC: return "pc"; case SCEI: return "scei"; case SUSE: return "suse"; + case Espressif: return "esp"; } llvm_unreachable("Invalid VendorType!"); @@ -371,6 +375,7 @@ Triple::ArchType Triple::getArchTypeForLLVMName(StringRef Name) { .Case("loongarch32", loongarch32) .Case("loongarch64", loongarch64) .Case("dxil", dxil) + .Case("xtensa", xtensa) .Default(UnknownArch); } @@ -511,6 +516,7 @@ static Triple::ArchType parseArch(StringRef ArchName) { .Case("loongarch32", Triple::loongarch32) .Case("loongarch64", Triple::loongarch64) .Case("dxil", Triple::dxil) + .Case("xtensa", Triple::xtensa) .Default(Triple::UnknownArch); // Some architectures require special parsing logic just to compute the @@ -543,6 +549,7 @@ static Triple::VendorType parseVendor(StringRef VendorName) { .Case("mesa", Triple::Mesa) .Case("suse", Triple::SUSE) .Case("oe", Triple::OpenEmbedded) + .Case("esp", Triple::Espressif) .Default(Triple::UnknownVendor); } @@ -835,6 +842,7 @@ static Triple::ObjectFormatType getDefaultFormat(const Triple &T) { case Triple::thumbeb: case Triple::ve: case Triple::xcore: + case Triple::xtensa: return Triple::ELF; case Triple::ppc64: @@ -1411,6 +1419,7 @@ static unsigned getArchPointerBitWidth(llvm::Triple::ArchType Arch) { case llvm::Triple::wasm32: case llvm::Triple::x86: case llvm::Triple::xcore: + case llvm::Triple::xtensa: return 32; case llvm::Triple::aarch64: @@ -1501,6 +1510,7 @@ Triple Triple::get32BitArchVariant() const { case Triple::wasm32: case Triple::x86: case Triple::xcore: + case Triple::xtensa: // Already 32-bit. break; @@ -1551,6 +1561,7 @@ Triple Triple::get64BitArchVariant() const { case Triple::tce: case Triple::tcele: case Triple::xcore: + case Triple::xtensa: T.setArch(UnknownArch); break; @@ -1651,6 +1662,7 @@ Triple Triple::getBigEndianArchVariant() const { case Triple::xcore: case Triple::ve: case Triple::csky: + case Triple::xtensa: // ARM is intentionally unsupported here, changing the architecture would // drop any arch suffixes. @@ -1760,6 +1772,7 @@ bool Triple::isLittleEndian() const { case Triple::x86: case Triple::x86_64: case Triple::xcore: + case Triple::xtensa: return true; default: return false; diff --git a/llvm/lib/Target/Xtensa/AsmParser/CMakeLists.txt b/llvm/lib/Target/Xtensa/AsmParser/CMakeLists.txt new file mode 100644 index 0000000000000..62bc6c2b99707 --- /dev/null +++ b/llvm/lib/Target/Xtensa/AsmParser/CMakeLists.txt @@ -0,0 +1,17 @@ +include_directories( ${CMAKE_CURRENT_BINARY_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/.. ) + +add_llvm_component_library(LLVMXtensaAsmParser + XtensaAsmParser.cpp + + LINK_COMPONENTS + MC + MCParser + Support + XtensaDesc + XtensaInfo + + ADD_TO_COMPONENT + Xtensa + ) + +add_dependencies(LLVMXtensaAsmParser XtensaCommonTableGen) diff --git a/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp b/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp new file mode 100644 index 0000000000000..c0672d1cbd752 --- /dev/null +++ b/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp @@ -0,0 +1,1316 @@ +//===- XtensaAsmParser.cpp - Parse Xtensa assembly to MCInst instructions -===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/XtensaMCExpr.h" +#include "MCTargetDesc/XtensaMCTargetDesc.h" +#include "MCTargetDesc/XtensaTargetStreamer.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCParser/MCAsmLexer.h" +#include "llvm/MC/MCParser/MCParsedAsmOperand.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/Casting.h" + +using namespace llvm; + +#define DEBUG_TYPE "xtensa-asm-parser" + +struct XtensaOperand; + +class XtensaAsmParser : public MCTargetAsmParser { + // Xtensa GNU assembler supports region definitions using + // .begin and .end directives. Currently only .literal_prefix regions are + // supported. + struct RegionInfo { + public: + SMLoc Loc; + StringRef RegionDirectiveName; + StringRef LiteralPrefixName; + RegionInfo() = default; + RegionInfo( SMLoc L, StringRef DirectiveName, StringRef Name = "") + : Loc(L), RegionDirectiveName(DirectiveName), LiteralPrefixName(Name) {} + }; + + // Stack of active region definitions. + SmallVector RegionInProgress; + + SMLoc getLoc() const { return getParser().getTok().getLoc(); } + + XtensaTargetStreamer &getTargetStreamer() { + MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer(); + return static_cast(TS); + } + + // Override MCTargetAsmParser. + bool ParseDirective(AsmToken DirectiveID) override; + bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override; + bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name, + SMLoc NameLoc, OperandVector &Operands) override; + bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, + OperandVector &Operands, MCStreamer &Out, + uint64_t &ErrorInfo, + bool MatchingInlineAsm) override; + unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, + unsigned Kind) override; + + bool processInstruction(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + +// Auto-generated instruction matching functions +#define GET_ASSEMBLER_HEADER +#include "XtensaGenAsmMatcher.inc" + + OperandMatchResultTy parseImmediate(OperandVector &Operands); + OperandMatchResultTy parseRegister(OperandVector &Operands, + bool AllowParens = false, bool SR = false, + bool UR = false); + OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands); + bool parseOperand(OperandVector &Operands, StringRef Mnemonic, + bool SR = false, bool UR = false); + bool ParseInstructionWithSR(ParseInstructionInfo &Info, StringRef Name, + SMLoc NameLoc, OperandVector &Operands); + OperandMatchResultTy tryParseRegister(unsigned &RegNo, SMLoc &StartLoc, + SMLoc &EndLoc) override { + return MatchOperand_NoMatch; + } + OperandMatchResultTy parsePCRelTarget(OperandVector &Operands); + bool checkRegister(unsigned RegNo); + bool parseLiteralDirective(SMLoc L); + bool parseBeginDirective(SMLoc L); + bool parseEndDirective(SMLoc L); + void onEndOfFile() override { + if (!RegionInProgress.empty()) { + Error(RegionInProgress.back().Loc, ".end of region is not found"); + } + } + +public: + enum XtensaMatchResultTy { + Match_Dummy = FIRST_TARGET_MATCH_RESULT_TY, +#define GET_OPERAND_DIAGNOSTIC_TYPES +#include "XtensaGenAsmMatcher.inc" +#undef GET_OPERAND_DIAGNOSTIC_TYPES + }; + + XtensaAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, + const MCInstrInfo &MII, const MCTargetOptions &Options) + : MCTargetAsmParser(Options, STI, MII) { + Parser.addAliasForDirective(".half", ".2byte"); + Parser.addAliasForDirective(".hword", ".2byte"); + Parser.addAliasForDirective(".word", ".4byte"); + Parser.addAliasForDirective(".dword", ".8byte"); + setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); + } + + bool hasWindowed() const { + return getSTI().getFeatureBits()[Xtensa::FeatureWindowed]; + }; + + bool hasSingleFloat() const { + return getSTI().getFeatureBits()[Xtensa::FeatureSingleFloat]; + }; + + bool hasLoop() const { + return getSTI().getFeatureBits()[Xtensa::FeatureLoop]; + }; + + bool hasMAC16() const { + return getSTI().getFeatureBits()[Xtensa::FeatureMAC16]; + }; + + bool hasBoolean() const { + return getSTI().getFeatureBits()[Xtensa::FeatureBoolean]; + }; + + bool hasDFPAccel() const { + return getSTI().getFeatureBits()[Xtensa::FeatureDFPAccel]; + }; + + bool hasS32C1I() const { + return getSTI().getFeatureBits()[Xtensa::FeatureS32C1I]; + }; + + bool hasTHREADPTR() const { + return getSTI().getFeatureBits()[Xtensa::FeatureTHREADPTR]; + }; + + bool hasExtendedL32R() const { + return getSTI().getFeatureBits()[Xtensa::FeatureExtendedL32R]; + } + + bool hasATOMCTL() const { + return getSTI().getFeatureBits()[Xtensa::FeatureATOMCTL]; + } + + bool hasMEMCTL() const { + return getSTI().getFeatureBits()[Xtensa::FeatureMEMCTL]; + } + + bool hasDebug() const { + return getSTI().getFeatureBits()[Xtensa::FeatureDebug]; + } + + bool hasException() const { + return getSTI().getFeatureBits()[Xtensa::FeatureException]; + } + + bool hasHighPriInterrupts() const { + return getSTI().getFeatureBits()[Xtensa::FeatureHighPriInterrupts]; + } + + bool hasCoprocessor() const { + return getSTI().getFeatureBits()[Xtensa::FeatureCoprocessor]; + } + + bool hasInterrupt() const { + return getSTI().getFeatureBits()[Xtensa::FeatureInterrupt]; + } + + bool hasRelocatableVector() const { + return getSTI().getFeatureBits()[Xtensa::FeatureRelocatableVector]; + } + + bool hasTimerInt() const { + return getSTI().getFeatureBits()[Xtensa::FeatureTimerInt]; + } + + bool hasPRID() const { + return getSTI().getFeatureBits()[Xtensa::FeaturePRID]; + } + + bool hasMiscSR() const { + return getSTI().getFeatureBits()[Xtensa::FeatureMiscSR]; + } +}; + +// Return true if Expr is in the range [MinValue, MaxValue]. +static bool inRange(const MCExpr *Expr, int64_t MinValue, int64_t MaxValue) { + if (auto *CE = dyn_cast(Expr)) { + int64_t Value = CE->getValue(); + return Value >= MinValue && Value <= MaxValue; + } + return false; +} + +struct XtensaOperand : public MCParsedAsmOperand { + + enum KindTy { + Token, + Register, + Immediate, + } Kind; + + struct RegOp { + unsigned RegNum; + }; + + struct ImmOp { + const MCExpr *Val; + }; + + SMLoc StartLoc, EndLoc; + union { + StringRef Tok; + RegOp Reg; + ImmOp Imm; + }; + + XtensaOperand(KindTy K) : MCParsedAsmOperand(), Kind(K) {} + +public: + XtensaOperand(const XtensaOperand &o) : MCParsedAsmOperand() { + Kind = o.Kind; + StartLoc = o.StartLoc; + EndLoc = o.EndLoc; + switch (Kind) { + case Register: + Reg = o.Reg; + break; + case Immediate: + Imm = o.Imm; + break; + case Token: + Tok = o.Tok; + break; + } + } + + bool isToken() const override { return Kind == Token; } + bool isReg() const override { return Kind == Register; } + bool isImm() const override { return Kind == Immediate; } + bool isMem() const override { return false; } + + bool isImm(int64_t MinValue, int64_t MaxValue) const { + return Kind == Immediate && inRange(getImm(), MinValue, MaxValue); + } + + bool isImm8() const { + //The addi instruction maybe expaned to addmi and addi. + return isImm((-32768 - 128), (32512 + 127)); + } + + bool isImm8_sh8() const { + return isImm(-32768, 32512) && + ((dyn_cast(getImm())->getValue() & 0xFF) == 0); + } + + bool isImm12() const { return isImm(-2048, 2047); } + + // Convert MOVI to literal load, when immediate is not in range (-2048, 2047) + bool isImm12m() const { + if (Kind == Immediate) + return true; + + return false; + } + + bool isOffset4m32() const { + return isImm(0, 60) && + ((dyn_cast(getImm())->getValue() & 0x3) == 0); + } + + bool isOffset8m8() const { return isImm(0, 255); } + + bool isOffset8m16() const { + return isImm(0, 510) && + ((dyn_cast(getImm())->getValue() & 0x1) == 0); + } + + bool isOffset8m32() const { + return isImm(0, 1020) && + ((dyn_cast(getImm())->getValue() & 0x3) == 0); + } + + bool isentry_imm12() const { return isImm(0, 32760); } + + bool isUimm4() const { return isImm(0, 15); } + + bool isUimm5() const { return isImm(0, 31); } + + bool isImm8n_7() const { return isImm(-8, 7); } + + bool isShimm1_31() const { return isImm(1, 31); } + + bool isImm16_31() const { return isImm(16, 31); } + + bool isImm1_16() const { return isImm(1, 16); } + + bool isImm1n_15() const { return (isImm(1, 15) || isImm(-1, -1)); } + + bool isImm32n_95() const { return isImm(-32, 95); } + + bool isImm64n_4n() const { + return isImm(-64, -4) && + ((dyn_cast(getImm())->getValue() & 0x3) == 0); + } + + bool isB4const() const { + if (Kind != Immediate) + return false; + if (auto *CE = dyn_cast(getImm())) { + int64_t Value = CE->getValue(); + switch (Value) { + case -1: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 10: + case 12: + case 16: + case 32: + case 64: + case 128: + case 256: + return true; + default: + return false; + } + } + return false; + } + + bool isB4constu() const { + if (Kind != Immediate) + return false; + if (auto *CE = dyn_cast(getImm())) { + int64_t Value = CE->getValue(); + switch (Value) { + case 32768: + case 65536: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 10: + case 12: + case 16: + case 32: + case 64: + case 128: + case 256: + return true; + default: + return false; + } + } + return false; + } + + bool isseimm7_22() const { return isImm(7, 22); } + + bool isSelect_256() const { return isImm(0, 255); } + + /// getStartLoc - Gets location of the first token of this operand + SMLoc getStartLoc() const override { return StartLoc; } + /// getEndLoc - Gets location of the last token of this operand + SMLoc getEndLoc() const override { return EndLoc; } + + unsigned getReg() const override { + assert(Kind == Register && "Invalid type access!"); + return Reg.RegNum; + } + + const MCExpr *getImm() const { + assert(Kind == Immediate && "Invalid type access!"); + return Imm.Val; + } + + StringRef getToken() const { + assert(Kind == Token && "Invalid type access!"); + return Tok; + } + + void print(raw_ostream &OS) const override { + switch (Kind) { + case Immediate: + OS << *getImm(); + break; + case Register: + OS << ""; + break; + case Token: + OS << "'" << getToken() << "'"; + break; + } + } + + static std::unique_ptr createToken(StringRef Str, SMLoc S) { + auto Op = std::make_unique(Token); + Op->Tok = Str; + Op->StartLoc = S; + Op->EndLoc = S; + return Op; + } + + static std::unique_ptr createReg(unsigned RegNo, SMLoc S, + SMLoc E) { + auto Op = std::make_unique(Register); + Op->Reg.RegNum = RegNo; + Op->StartLoc = S; + Op->EndLoc = E; + return Op; + } + + static std::unique_ptr createImm(const MCExpr *Val, SMLoc S, + SMLoc E) { + auto Op = std::make_unique(Immediate); + Op->Imm.Val = Val; + Op->StartLoc = S; + Op->EndLoc = E; + return Op; + } + + void addExpr(MCInst &Inst, const MCExpr *Expr) const { + assert(Expr && "Expr shouldn't be null!"); + int64_t Imm = 0; + bool IsConstant = false; + + if (auto *CE = dyn_cast(Expr)) { + IsConstant = true; + Imm = CE->getValue(); + } + + if (IsConstant) + Inst.addOperand(MCOperand::createImm(Imm)); + else + Inst.addOperand(MCOperand::createExpr(Expr)); + } + + // Used by the TableGen Code + void addRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getReg())); + } + + void addImmOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + addExpr(Inst, getImm()); + } +}; + +#define GET_REGISTER_MATCHER +#define GET_MATCHER_IMPLEMENTATION +#include "XtensaGenAsmMatcher.inc" + +unsigned XtensaAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp, + unsigned Kind) { + return Match_InvalidOperand; +} + +static SMLoc RefineErrorLoc(const SMLoc Loc, const OperandVector &Operands, + uint64_t ErrorInfo) { + if (ErrorInfo != ~0ULL && ErrorInfo < Operands.size()) { + SMLoc ErrorLoc = Operands[ErrorInfo]->getStartLoc(); + if (ErrorLoc == SMLoc()) + return Loc; + return ErrorLoc; + } + return Loc; +} + +bool XtensaAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + Inst.setLoc(IDLoc); + const unsigned Opcode = Inst.getOpcode(); + + switch (Opcode) { + case Xtensa::ADDI: { + int64_t Imm = Inst.getOperand(2).getImm(); + // Expand 16-bit immediate in ADDI instruction: + // ADDI rd, rs, imm - > ADMI rd, rs, (imm & 0xff00); ADDI rd, rd, (imm & 0xff) + if ((Imm < -128) || (Imm > 127)) { + unsigned DReg = Inst.getOperand(0).getReg(); + unsigned SReg = Inst.getOperand(1).getReg(); + MCInst ADDMIInst; + MCInst ADDIInst; + int64_t ImmHi = Imm & (~((uint64_t)0xff)); + int64_t ImmLo = Imm & 0xff; + + if (ImmLo > 127) { + ImmHi += 0x100; + ImmLo = ImmLo - 0x100; + } + + ADDMIInst.setOpcode(Xtensa::ADDMI); + ADDMIInst.addOperand(MCOperand::createReg(DReg)); + ADDMIInst.addOperand(MCOperand::createReg(SReg)); + ADDMIInst.addOperand(MCOperand::createImm(ImmHi)); + ADDMIInst.setLoc(IDLoc); + + Out.emitInstruction(ADDMIInst, *STI); + + ADDIInst.setOpcode(Xtensa::ADDI); + ADDIInst.addOperand(MCOperand::createReg(DReg)); + ADDIInst.addOperand(MCOperand::createReg(DReg)); + ADDIInst.addOperand(MCOperand::createImm(ImmLo)); + ADDIInst.setLoc(IDLoc); + + Inst = ADDIInst; + } + } break; + case Xtensa::L32R: { + const MCSymbolRefExpr *OpExpr = + (const MCSymbolRefExpr *)Inst.getOperand(1).getExpr(); + XtensaMCExpr::VariantKind Kind = XtensaMCExpr::VK_Xtensa_None; + const MCExpr *NewOpExpr = XtensaMCExpr::create(OpExpr, Kind, getContext()); + Inst.getOperand(1).setExpr(NewOpExpr); + } break; + case Xtensa::MOVI: { + XtensaTargetStreamer &TS = this->getTargetStreamer(); + + //Expand MOVI operand + if (!Inst.getOperand(1).isExpr()) { + uint64_t ImmOp64 = Inst.getOperand(1).getImm(); + int32_t Imm = ImmOp64; + if ((Imm < -2048) || (Imm > 2047)) { + XtensaTargetStreamer &TS = this->getTargetStreamer(); + MCInst TmpInst; + TmpInst.setLoc(IDLoc); + TmpInst.setOpcode(Xtensa::L32R); + const MCExpr *Value = MCConstantExpr::create(ImmOp64, getContext()); + MCSymbol *Sym = getContext().createTempSymbol(); + const MCExpr *Expr = MCSymbolRefExpr::create( + Sym, MCSymbolRefExpr::VK_None, getContext()); + const MCExpr *OpExpr = XtensaMCExpr::create( + Expr, XtensaMCExpr::VK_Xtensa_None, getContext()); + TmpInst.addOperand(Inst.getOperand(0)); + MCOperand Op1 = MCOperand::createExpr(OpExpr); + TmpInst.addOperand(Op1); + TS.emitLiteralLabel(Sym, IDLoc); + TS.emitLiteral(Value, IDLoc); + Inst = TmpInst; + } + } else { + MCInst TmpInst; + TmpInst.setLoc(IDLoc); + TmpInst.setOpcode(Xtensa::L32R); + const MCExpr *Value = Inst.getOperand(1).getExpr(); + MCSymbol *Sym = getContext().createTempSymbol(); + const MCExpr *Expr = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + const MCExpr *OpExpr = XtensaMCExpr::create( + Expr, XtensaMCExpr::VK_Xtensa_None, getContext()); + TmpInst.addOperand(Inst.getOperand(0)); + MCOperand Op1 = MCOperand::createExpr(OpExpr); + TmpInst.addOperand(Op1); + Inst = TmpInst; + TS.emitLiteralLabel(Sym, IDLoc); + TS.emitLiteral(Value, IDLoc); + } + } break; + default: + break; + } + return true; +} + +bool XtensaAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, + OperandVector &Operands, + MCStreamer &Out, + uint64_t &ErrorInfo, + bool MatchingInlineAsm) { + MCInst Inst; + auto Result = + MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm); + + switch (Result) { + default: + break; + case Match_Success: + processInstruction(Inst, IDLoc, Out, STI); + Inst.setLoc(IDLoc); + Out.emitInstruction(Inst, getSTI()); + return false; + case Match_MissingFeature: + return Error(IDLoc, "instruction use requires an option to be enabled"); + case Match_MnemonicFail: + return Error(IDLoc, "unrecognized instruction mnemonic"); + case Match_InvalidOperand: { + SMLoc ErrorLoc = IDLoc; + if (ErrorInfo != ~0U) { + if (ErrorInfo >= Operands.size()) + return Error(ErrorLoc, "too few operands for instruction"); + + ErrorLoc = ((XtensaOperand &)*Operands[ErrorInfo]).getStartLoc(); + if (ErrorLoc == SMLoc()) + ErrorLoc = IDLoc; + } + return Error(ErrorLoc, "invalid operand for instruction"); + } + case Match_InvalidImm8: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [-32896, 32639]"); + case Match_InvalidImm8_sh8: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [-32768, 32512], first 8 bits " + "should be zero"); + case Match_InvalidB4const: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected b4const immediate"); + case Match_InvalidB4constu: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected b4constu immediate"); + case Match_InvalidImm12: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [-2048, 2047]"); + case Match_InvalidImm1_16: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [1, 16]"); + case Match_InvalidImm1n_15: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [-1, 15] except 0"); + case Match_InvalidImm32n_95: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [-32, 95] except 0"); + case Match_InvalidImm64n_4n: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [-64, -4]"); + case Match_InvalidImm8n_7: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [-8, 7]"); + case Match_InvalidShimm1_31: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [1, 31]"); + case Match_InvalidUimm4: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [0, 15]"); + case Match_InvalidUimm5: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [0, 31]"); + case Match_InvalidOffset8m8: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [0, 255]"); + case Match_InvalidOffset8m16: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [0, 510], first bit " + "should be zero"); + case Match_InvalidOffset8m32: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [0, 1020], first 2 bits " + "should be zero"); + case Match_InvalidOffset4m32: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [0, 60], first 2 bits " + "should be zero"); + case Match_Invalidentry_imm12: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [0, 32760]"); + case Match_Invalidseimm7_22: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [7, 22]"); + case Match_InvalidSelect_256: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range [0, 255]"); + } + + report_fatal_error("Unknown match type detected!"); +} + +OperandMatchResultTy +XtensaAsmParser::parsePCRelTarget(OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + LLVM_DEBUG(dbgs() << "parsePCRelTarget\n"); + + SMLoc S = getLexer().getLoc(); + + // Expressions are acceptable + const MCExpr *Expr = nullptr; + if (Parser.parseExpression(Expr)) { + // We have no way of knowing if a symbol was consumed so we must ParseFail + return MatchOperand_ParseFail; + } + + // Currently not support constants + if (Expr->getKind() == MCExpr::ExprKind::Constant) { + Error(getLoc(), "unknown operand"); + return MatchOperand_ParseFail; + } + + Operands.push_back(XtensaOperand::createImm(Expr, S, getLexer().getLoc())); + return MatchOperand_Success; +} + +bool XtensaAsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc, + SMLoc &EndLoc) { + const AsmToken &Tok = getParser().getTok(); + StartLoc = Tok.getLoc(); + EndLoc = Tok.getEndLoc(); + RegNo = 0; + StringRef Name = getLexer().getTok().getIdentifier(); + + if ((!MatchRegisterName(Name)) && (!MatchRegisterAltName(Name))) { + getParser().Lex(); // Eat identifier token. + return false; + } + + return Error(StartLoc, "invalid register name"); +} + +OperandMatchResultTy XtensaAsmParser::parseRegister(OperandVector &Operands, + bool AllowParens, bool SR, + bool UR) { + SMLoc FirstS = getLoc(); + bool HadParens = false; + AsmToken Buf[2]; + std::string RegName = ""; + int64_t Num; + bool IsIdentifier = false; + + // If this a parenthesised register name is allowed, parse it atomically + if (AllowParens && getLexer().is(AsmToken::LParen)) { + size_t ReadCount = getLexer().peekTokens(Buf); + if (ReadCount == 2 && Buf[1].getKind() == AsmToken::RParen) { + if ((Buf[0].getKind() == AsmToken::Integer) && (!SR) && (!UR)) + return MatchOperand_NoMatch; + HadParens = true; + getParser().Lex(); // Eat '(' + } + } + + switch (getLexer().getKind()) { + default: + return MatchOperand_NoMatch; + case AsmToken::Integer: + case AsmToken::LParen: + if ((!SR) && (!UR)) + return MatchOperand_NoMatch; + const MCExpr *Res; + + if (getParser().parseExpression(Res)) + return MatchOperand_ParseFail; + + if (!Res->evaluateAsAbsolute(Num)) + return MatchOperand_NoMatch; + + // Parse case when we expect UR operand as special case, + // because SR and UR registers may have the same number + // and such situation may lead to confilct + if (UR) { + if (Num == 0) + RegName = "GPIO_OUT"; + if (Num == 230) + RegName = "EXPSTATE"; + if (Num == 231) + RegName = "THREADPTR"; + if (Num == 232) + RegName = "FCR"; + if (Num == 233) + RegName = "FSR"; + if (Num == 234) + RegName = "F64R_LO"; + if (Num == 235) + RegName = "F64R_HI"; + if (Num == 236) + RegName = "F64S"; + } else + RegName = std::to_string(Num); + break; + case AsmToken::Identifier: + IsIdentifier = true; + RegName = getLexer().getTok().getIdentifier().str(); + break; + } + + unsigned RegNo = MatchRegisterName(RegName); + if (RegNo == 0) + RegNo = MatchRegisterAltName(RegName); + + if (RegNo == 0) { + if (HadParens) + getLexer().UnLex(Buf[0]); + return MatchOperand_NoMatch; + } + + if (!checkRegister(RegNo)) { + return MatchOperand_NoMatch; + } + + if (HadParens) + Operands.push_back(XtensaOperand::createToken("(", FirstS)); + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + + if (IsIdentifier) + getLexer().Lex(); + + Operands.push_back(XtensaOperand::createReg(RegNo, S, E)); + + if (HadParens) { + getParser().Lex(); // Eat ')' + Operands.push_back(XtensaOperand::createToken(")", getLoc())); + } + + return MatchOperand_Success; +} + +OperandMatchResultTy XtensaAsmParser::parseImmediate(OperandVector &Operands) { + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + const MCExpr *Res; + + switch (getLexer().getKind()) { + default: + return MatchOperand_NoMatch; + case AsmToken::LParen: + case AsmToken::Minus: + case AsmToken::Plus: + case AsmToken::Tilde: + case AsmToken::Integer: + case AsmToken::String: + if (getParser().parseExpression(Res)) + return MatchOperand_ParseFail; + break; + case AsmToken::Identifier: { + if (getParser().parseExpression(Res)) + return MatchOperand_ParseFail; + break; + } + case AsmToken::Percent: + return parseOperandWithModifier(Operands); + } + + Operands.push_back(XtensaOperand::createImm(Res, S, E)); + return MatchOperand_Success; +} + +OperandMatchResultTy +XtensaAsmParser::parseOperandWithModifier(OperandVector &Operands) { + return MatchOperand_ParseFail; +} + +/// Looks at a token type and creates the relevant operand +/// from this information, adding to Operands. +/// If operand was parsed, returns false, else true. +bool XtensaAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic, + bool SR, bool UR) { + // Check if the current operand has a custom associated parser, if so, try to + // custom parse the operand, or fallback to the general approach. + OperandMatchResultTy ResTy = MatchOperandParserImpl(Operands, Mnemonic); + if (ResTy == MatchOperand_Success) + return false; + + // If there wasn't a custom match, try the generic matcher below. Otherwise, + // there was a match, but an error occurred, in which case, just return that + // the operand parsing failed. + if (ResTy == MatchOperand_ParseFail) + return true; + + // Attempt to parse token as register + if (parseRegister(Operands, true, SR, UR) == MatchOperand_Success) + return false; + + // Attempt to parse token as an immediate + if (parseImmediate(Operands) == MatchOperand_Success) { + return false; + } + + // Finally we have exhausted all options and must declare defeat. + Error(getLoc(), "unknown operand"); + return true; +} + +bool XtensaAsmParser::ParseInstructionWithSR(ParseInstructionInfo &Info, + StringRef Name, SMLoc NameLoc, + OperandVector &Operands) { + bool IsSR = Name.startswith("wsr") || Name.startswith("rsr") || + Name.startswith("xsr"); + bool IsUR = Name.startswith("wur") || Name.startswith("rur"); + + if ((Name.startswith("wsr.") || Name.startswith("rsr.") || + Name.startswith("xsr.") || Name.startswith("rur.") || + Name.startswith("wur.")) && + (Name.size() > 4)) { + // Parse case when instruction name is concatenated with SR register + // name, like "wsr.sar a1" + + // First operand is token for instruction + Operands.push_back(XtensaOperand::createToken(Name.take_front(3), NameLoc)); + + StringRef RegName = Name.drop_front(4); + unsigned RegNo = MatchRegisterName(RegName); + + if (RegNo == 0) + RegNo = MatchRegisterAltName(RegName); + + if (RegNo == 0) { + Error(NameLoc, "invalid register name"); + return true; + } + + if (!checkRegister(RegNo)) { + Error(NameLoc, "invalid register name"); + return true; + } + + // Parse operand + if (parseOperand(Operands, Name)) + return true; + + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + Operands.push_back(XtensaOperand::createReg(RegNo, S, E)); + } else { + // First operand is token for instruction + Operands.push_back(XtensaOperand::createToken(Name, NameLoc)); + + // Parse first operand + if (parseOperand(Operands, Name)) + return true; + + if (!getLexer().is(AsmToken::Comma)) { + SMLoc Loc = getLexer().getLoc(); + getParser().eatToEndOfStatement(); + return Error(Loc, "unexpected token"); + } + + getLexer().Lex(); + + // Parse second operand + if (parseOperand(Operands, Name, IsSR, IsUR)) + return true; + } + + if (getLexer().isNot(AsmToken::EndOfStatement)) { + SMLoc Loc = getLexer().getLoc(); + getParser().eatToEndOfStatement(); + return Error(Loc, "unexpected token"); + } + + getParser().Lex(); // Consume the EndOfStatement. + return false; +} + +bool XtensaAsmParser::ParseInstruction(ParseInstructionInfo &Info, + StringRef Name, SMLoc NameLoc, + OperandVector &Operands) { + if (Name.startswith("wsr") || Name.startswith("rsr") || + Name.startswith("xsr") || Name.startswith("rur") || + Name.startswith("wur")) { + return ParseInstructionWithSR(Info, Name, NameLoc, Operands); + } + + // First operand is token for instruction + Operands.push_back(XtensaOperand::createToken(Name, NameLoc)); + + // If there are no more operands, then finish + if (getLexer().is(AsmToken::EndOfStatement)) + return false; + + // Parse first operand + if (parseOperand(Operands, Name)) + return true; + + // Parse until end of statement, consuming commas between operands + while (getLexer().is(AsmToken::Comma)) { + // Consume comma token + getLexer().Lex(); + + // Parse next operand + if (parseOperand(Operands, Name)) + return true; + } + + if (getLexer().isNot(AsmToken::EndOfStatement)) { + SMLoc Loc = getLexer().getLoc(); + getParser().eatToEndOfStatement(); + return Error(Loc, "unexpected token"); + } + + getParser().Lex(); // Consume the EndOfStatement. + return false; +} + +bool XtensaAsmParser::parseLiteralDirective(SMLoc L) { + MCAsmParser &Parser = getParser(); + MCSymbol *Sym; + const MCExpr *Value; + SMLoc LiteralLoc = getLexer().getLoc(); + XtensaTargetStreamer &TS = this->getTargetStreamer(); + + if (Parser.parseExpression(Value)) + return true; + + const MCSymbolRefExpr *SE = dyn_cast(Value); + if (!SE) + return Error(LiteralLoc, "literal label must be a symbol"); + else { + Sym = getContext().getOrCreateSymbol(SE->getSymbol().getName()); + } + + if (Parser.parseToken(AsmToken::Comma, "expected comma")) + return true; + + SMLoc OpcodeLoc = getLexer().getLoc(); + if (parseOptionalToken(AsmToken::EndOfStatement)) + return Error(OpcodeLoc, "expected value"); + + if (Parser.parseExpression(Value)) + return true; + + TS.emitLiteralLabel(Sym, LiteralLoc); + TS.emitLiteral(Value, LiteralLoc); + + return false; +} + +bool XtensaAsmParser::parseBeginDirective(SMLoc L) { + MCAsmParser &Parser = getParser(); + const MCExpr *Value; + SMLoc BeginLoc = getLexer().getLoc(); + XtensaTargetStreamer &TS = this->getTargetStreamer(); + + if (Parser.parseExpression(Value)) + return true; + + const MCSymbolRefExpr *SE = dyn_cast(Value); + if (!SE) + return Error(BeginLoc, "region option must be a symbol"); + + StringRef RegionDirectiveName = SE->getSymbol().getName(); + + if (RegionDirectiveName == "literal_prefix") { + + SMLoc OpcodeLoc = getLexer().getLoc(); + if (parseOptionalToken(AsmToken::EndOfStatement)) + return Error(OpcodeLoc, "expected literal section name"); + + if (Parser.parseExpression(Value)) + return true; + + OpcodeLoc = getLexer().getLoc(); + SE = dyn_cast(Value); + if (!SE) + return Error(OpcodeLoc, "literal_prefix name must be a symbol"); + + StringRef LiteralPrefixName = SE->getSymbol().getName(); + TS.setLiteralSectionPrefix(LiteralPrefixName); + RegionInProgress.emplace_back(BeginLoc, RegionDirectiveName, LiteralPrefixName); + } else { + return Error(BeginLoc, "unsupported region directive"); + } + + return false; +} + +bool XtensaAsmParser::parseEndDirective(SMLoc L) { + MCAsmParser &Parser = getParser(); + const MCExpr *Value; + SMLoc EndLoc = getLexer().getLoc(); + XtensaTargetStreamer &TS = this->getTargetStreamer(); + + if (Parser.parseExpression(Value)) + return true; + + const MCSymbolRefExpr *SE = dyn_cast(Value); + if (!SE) + return Error(EndLoc, "region option must be a symbol"); + + StringRef RegionDirectiveName = SE->getSymbol().getName(); + + if (RegionInProgress.empty()) + return Error(EndLoc, ".end of the region without .begin"); + else { + RegionInfo Region = RegionInProgress.pop_back_val(); + + if (RegionInProgress.empty()) + TS.setLiteralSectionPrefix(""); + else + TS.setLiteralSectionPrefix(Region.LiteralPrefixName); + + if (RegionDirectiveName != Region.RegionDirectiveName) { + return Error(EndLoc, ".end directive differs from .begin directive"); + } + } + + // Error: does not match begin literal_prefix + return false; +} + +bool XtensaAsmParser::ParseDirective(AsmToken DirectiveID) { + StringRef IDVal = DirectiveID.getString(); + SMLoc Loc = getLexer().getLoc(); + + if (IDVal == ".literal_position") { + // We currently push literals in literal section which name depends on name + // of the current section. + // So, assume that we may skip this directive. + return false; + } + + if (IDVal == ".literal") { + parseLiteralDirective(Loc); + return false; + } + + if (IDVal == ".begin") { + parseBeginDirective(Loc); + return false; + } + + if (IDVal == ".end") { + parseEndDirective(Loc); + return false; + } + + return true; +} + +// Verify SR and UR +bool XtensaAsmParser::checkRegister(unsigned RegNo) { + StringRef CPU = getSTI().getCPU(); + unsigned NumIntLevels = 0; + unsigned NumTimers = 0; + unsigned NumMiscSR = 0; + bool IsESP32 = false; + bool IsESP32S2 = false; + bool IsESP32S3 = false; + bool Res = true; + + // Assume that CPU is esp32 by default + if ((CPU == "esp32") || (CPU == "")) { + NumIntLevels = 6; + NumTimers = 3; + NumMiscSR = 4; + IsESP32 = true; + } else if (CPU == "esp32s2") { + NumIntLevels = 6; + NumTimers = 3; + NumMiscSR = 4; + IsESP32S2 = true; + } else if (CPU == "esp32s3") { + NumIntLevels = 6; + NumTimers = 3; + NumMiscSR = 4; + IsESP32S3 = true; + } else if (CPU == "esp8266") { + NumIntLevels = 2; + NumTimers = 1; + } + + switch (RegNo) { + case Xtensa::LBEG: + case Xtensa::LEND: + case Xtensa::LCOUNT: + Res = hasLoop(); + break; + case Xtensa::BREG: + Res = hasBoolean(); + break; + case Xtensa::LITBASE: + Res = hasExtendedL32R(); + break; + case Xtensa::SCOMPARE1: + Res = hasS32C1I(); + break; + case Xtensa::ACCLO: + case Xtensa::ACCHI: + case Xtensa::M0: + case Xtensa::M1: + case Xtensa::M2: + case Xtensa::M3: + Res = hasMAC16(); + break; + case Xtensa::WINDOWBASE: + case Xtensa::WINDOWSTART: + Res = hasWindowed(); + break; + case Xtensa::IBREAKENABLE: + case Xtensa::IBREAKA0: + case Xtensa::IBREAKA1: + case Xtensa::DBREAKA0: + case Xtensa::DBREAKA1: + case Xtensa::DBREAKC0: + case Xtensa::DBREAKC1: + case Xtensa::DEBUGCAUSE: + case Xtensa::ICOUNT: + case Xtensa::ICOUNTLEVEL: + Res = hasDebug(); + break; + case Xtensa::ATOMCTL: + Res = hasATOMCTL(); + break; + case Xtensa::MEMCTL: + Res = hasMEMCTL(); + break; + case Xtensa::EPC1: + Res = hasException(); + break; + case Xtensa::EPC2: + case Xtensa::EPC3: + case Xtensa::EPC4: + case Xtensa::EPC5: + case Xtensa::EPC6: + case Xtensa::EPC7: + Res = hasHighPriInterrupts(); + Res = Res & (NumIntLevels >= (RegNo - Xtensa::EPC1)); + break; + case Xtensa::EPS2: + case Xtensa::EPS3: + case Xtensa::EPS4: + case Xtensa::EPS5: + case Xtensa::EPS6: + case Xtensa::EPS7: + Res = hasHighPriInterrupts(); + Res = Res & (NumIntLevels > (RegNo - Xtensa::EPS2)); + break; + case Xtensa::EXCSAVE1: + Res = hasException(); + break; + case Xtensa::EXCSAVE2: + case Xtensa::EXCSAVE3: + case Xtensa::EXCSAVE4: + case Xtensa::EXCSAVE5: + case Xtensa::EXCSAVE6: + case Xtensa::EXCSAVE7: + Res = hasHighPriInterrupts(); + Res = Res & (NumIntLevels >= (RegNo - Xtensa::EXCSAVE1)); + break; + case Xtensa::DEPC: + case Xtensa::EXCCAUSE: + case Xtensa::EXCVADDR: + Res = hasException(); + break; + case Xtensa::CPENABLE: + Res = hasCoprocessor(); + break; + case Xtensa::VECBASE: + Res = hasRelocatableVector(); + break; + case Xtensa::CCOUNT: + Res = hasTimerInt(); + Res &= (NumTimers > 0); + break; + case Xtensa::CCOMPARE0: + case Xtensa::CCOMPARE1: + case Xtensa::CCOMPARE2: + Res = hasTimerInt(); + Res &= (NumTimers > (RegNo - Xtensa::CCOMPARE0)); + break; + case Xtensa::PRID: + Res = hasPRID(); + break; + case Xtensa::INTSET: + case Xtensa::INTCLEAR: + case Xtensa::INTENABLE: + Res = hasInterrupt(); + break; + case Xtensa::MISC0: + case Xtensa::MISC1: + case Xtensa::MISC2: + case Xtensa::MISC3: + Res = hasMiscSR(); + Res &= (NumMiscSR > (RegNo - Xtensa::MISC0)); + break; + case Xtensa::THREADPTR: + Res = hasTHREADPTR(); + break; + case Xtensa::GPIO_OUT: + Res = IsESP32S2 || IsESP32S3; + break; + case Xtensa::EXPSTATE: + Res = IsESP32; + break; + case Xtensa::FCR: + case Xtensa::FSR: + Res = hasSingleFloat(); + break; + case Xtensa::F64R_LO: + case Xtensa::F64R_HI: + case Xtensa::F64S: + Res = hasDFPAccel(); + break; + } + + return Res; +} + +// Force static initialization. +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeXtensaAsmParser() { + RegisterMCAsmParser X(TheXtensaTarget); +} diff --git a/llvm/lib/Target/Xtensa/CMakeLists.txt b/llvm/lib/Target/Xtensa/CMakeLists.txt new file mode 100644 index 0000000000000..873de70425a73 --- /dev/null +++ b/llvm/lib/Target/Xtensa/CMakeLists.txt @@ -0,0 +1,56 @@ +add_llvm_component_group(Xtensa) + +set(LLVM_TARGET_DEFINITIONS Xtensa.td) + +tablegen(LLVM XtensaGenAsmMatcher.inc -gen-asm-matcher) +tablegen(LLVM XtensaGenAsmWriter.inc -gen-asm-writer) +tablegen(LLVM XtensaGenCallingConv.inc -gen-callingconv) +tablegen(LLVM XtensaGenDAGISel.inc -gen-dag-isel) +tablegen(LLVM XtensaGenDisassemblerTables.inc -gen-disassembler) +tablegen(LLVM XtensaGenInstrInfo.inc -gen-instr-info) +tablegen(LLVM XtensaGenMCCodeEmitter.inc -gen-emitter) +tablegen(LLVM XtensaGenRegisterInfo.inc -gen-register-info) +tablegen(LLVM XtensaGenSubtargetInfo.inc -gen-subtarget) + +add_public_tablegen_target(XtensaCommonTableGen) + +add_llvm_target(XtensaCodeGen + XtensaAsmPrinter.cpp + XtensaConstantPoolValue.cpp + XtensaConstantIsland.cpp + XtensaESP32PSRAMFix.cpp + XtensaFixupHWLoops.cpp + XtensaFrameLowering.cpp + XtensaHardwareLoops.cpp + XtensaInstrInfo.cpp + XtensaISelDAGToDAG.cpp + XtensaISelLowering.cpp + XtensaMachineFunctionInfo.cpp + XtensaMCInstLower.cpp + XtensaRegisterInfo.cpp + XtensaSizeReductionPass.cpp + XtensaSubtarget.cpp + XtensaTargetMachine.cpp + XtensaTargetObjectFile.cpp + XtensaTargetTransformInfo.cpp + + LINK_COMPONENTS + AsmPrinter + CodeGen + Core + MC + SelectionDAG + Support + Target + XtensaDesc + XtensaInfo + + ADD_TO_COMPONENT + Xtensa + ) + +add_subdirectory(AsmParser) +add_subdirectory(Disassembler) +add_subdirectory(MCTargetDesc) +add_subdirectory(TargetInfo) + diff --git a/llvm/lib/Target/Xtensa/Disassembler/CMakeLists.txt b/llvm/lib/Target/Xtensa/Disassembler/CMakeLists.txt new file mode 100644 index 0000000000000..43f235b7cd31d --- /dev/null +++ b/llvm/lib/Target/Xtensa/Disassembler/CMakeLists.txt @@ -0,0 +1,11 @@ +add_llvm_component_library(LLVMXtensaDisassembler + XtensaDisassembler.cpp + + LINK_COMPONENTS + MCDisassembler + Support + XtensaInfo + + ADD_TO_COMPONENT + Xtensa + ) diff --git a/llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp b/llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp new file mode 100644 index 0000000000000..686454ce793a6 --- /dev/null +++ b/llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp @@ -0,0 +1,700 @@ +//===-- XtensaDisassembler.cpp - Disassembler for Xtensa ------------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the XtensaDisassembler class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/XtensaMCTargetDesc.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDecoderOps.h" +#include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; + +#define DEBUG_TYPE "Xtensa-disassembler" + +using DecodeStatus = MCDisassembler::DecodeStatus; + +namespace { + +class XtensaDisassembler : public MCDisassembler { + bool IsLittleEndian; + +public: + XtensaDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, bool isLE) + : MCDisassembler(STI, Ctx), IsLittleEndian(isLE) {} + + bool hasDensity() const { + return STI.getFeatureBits()[Xtensa::FeatureDensity]; + } + + DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef Bytes, uint64_t Address, + raw_ostream &CStream) const override; +}; +} // end anonymous namespace + +static MCDisassembler *createXtensaDisassembler(const Target &T, + const MCSubtargetInfo &STI, + MCContext &Ctx) { + return new XtensaDisassembler(STI, Ctx, true); +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeXtensaDisassembler() { + TargetRegistry::RegisterMCDisassembler(TheXtensaTarget, + createXtensaDisassembler); +} + +static const unsigned ARDecoderTable[] = { + Xtensa::A0, Xtensa::SP, Xtensa::A2, Xtensa::A3, Xtensa::A4, Xtensa::A5, + Xtensa::A6, Xtensa::A7, Xtensa::A8, Xtensa::A9, Xtensa::A10, Xtensa::A11, + Xtensa::A12, Xtensa::A13, Xtensa::A14, Xtensa::A15}; + +static DecodeStatus DecodeARRegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo >= array_lengthof(ARDecoderTable)) + return MCDisassembler::Fail; + + unsigned Reg = ARDecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static const unsigned FPRDecoderTable[] = { + Xtensa::F0, Xtensa::F1, Xtensa::F2, Xtensa::F3, Xtensa::F4, Xtensa::F5, + Xtensa::F6, Xtensa::F7, Xtensa::F8, Xtensa::F9, Xtensa::F10, Xtensa::F11, + Xtensa::F12, Xtensa::F13, Xtensa::F14, Xtensa::F15}; + +static DecodeStatus DecodeFPRRegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo >= array_lengthof(FPRDecoderTable)) + return MCDisassembler::Fail; + + unsigned Reg = FPRDecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static const unsigned BRDecoderTable[] = { + Xtensa::B0, Xtensa::B1, Xtensa::B2, Xtensa::B3, Xtensa::B4, Xtensa::B5, + Xtensa::B6, Xtensa::B7, Xtensa::B8, Xtensa::B9, Xtensa::B10, Xtensa::B11, + Xtensa::B12, Xtensa::B13, Xtensa::B14, Xtensa::B15}; + +static DecodeStatus DecodeBRRegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo >= array_lengthof(BRDecoderTable)) + return MCDisassembler::Fail; + + unsigned Reg = BRDecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static const unsigned MRDecoderTable[] = {Xtensa::M0, Xtensa::M1, Xtensa::M2, + Xtensa::M3}; + +static DecodeStatus DecodeMRRegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo >= array_lengthof(MRDecoderTable)) + return MCDisassembler::Fail; + + unsigned Reg = MRDecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static const unsigned MR01DecoderTable[] = {Xtensa::M0, Xtensa::M1}; + +static DecodeStatus DecodeMR01RegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 2) + return MCDisassembler::Fail; + + unsigned Reg = MR01DecoderTable[RegNo]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static const unsigned MR23DecoderTable[] = {Xtensa::M2, Xtensa::M3}; + +static DecodeStatus DecodeMR23RegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + if ((RegNo < 2) || (RegNo > 3)) + return MCDisassembler::Fail; + + unsigned Reg = MR23DecoderTable[RegNo - 2]; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +// Verify SR and UR +bool CheckRegister(unsigned RegNo, MCSubtargetInfo STI) { + StringRef CPU = STI.getCPU(); + unsigned NumIntLevels = 0; + unsigned NumTimers = 0; + unsigned NumMiscSR = 0; + bool IsESP32 = false; + bool IsESP32S2 = false; + bool Res = true; + + // Assume that CPU is esp32 by default + if ((CPU == "esp32") || (CPU == "")) { + NumIntLevels = 6; + NumTimers = 3; + NumMiscSR = 4; + IsESP32 = true; + } else if (CPU == "esp32s2") { + NumIntLevels = 6; + NumTimers = 3; + NumMiscSR = 4; + IsESP32S2 = true; + } else if (CPU == "esp8266") { + NumIntLevels = 2; + NumTimers = 1; + } + + switch (RegNo) { + case Xtensa::LBEG: + case Xtensa::LEND: + case Xtensa::LCOUNT: + Res = STI.getFeatureBits()[Xtensa::FeatureLoop]; + break; + case Xtensa::BREG: + Res = STI.getFeatureBits()[Xtensa::FeatureBoolean]; + break; + case Xtensa::LITBASE: + Res = STI.getFeatureBits()[Xtensa::FeatureExtendedL32R]; + break; + case Xtensa::SCOMPARE1: + Res = STI.getFeatureBits()[Xtensa::FeatureS32C1I]; + break; + case Xtensa::ACCLO: + case Xtensa::ACCHI: + case Xtensa::M0: + case Xtensa::M1: + case Xtensa::M2: + case Xtensa::M3: + Res = STI.getFeatureBits()[Xtensa::FeatureMAC16]; + break; + case Xtensa::WINDOWBASE: + case Xtensa::WINDOWSTART: + Res = STI.getFeatureBits()[Xtensa::FeatureWindowed]; + break; + case Xtensa::IBREAKENABLE: + case Xtensa::IBREAKA0: + case Xtensa::IBREAKA1: + case Xtensa::DBREAKA0: + case Xtensa::DBREAKA1: + case Xtensa::DBREAKC0: + case Xtensa::DBREAKC1: + case Xtensa::DEBUGCAUSE: + case Xtensa::ICOUNT: + case Xtensa::ICOUNTLEVEL: + Res = STI.getFeatureBits()[Xtensa::FeatureDebug]; + break; + case Xtensa::ATOMCTL: + Res = STI.getFeatureBits()[Xtensa::FeatureATOMCTL]; + break; + case Xtensa::MEMCTL: + Res = STI.getFeatureBits()[Xtensa::FeatureMEMCTL]; + break; + case Xtensa::EPC1: + Res = STI.getFeatureBits()[Xtensa::FeatureException]; + break; + case Xtensa::EPC2: + case Xtensa::EPC3: + case Xtensa::EPC4: + case Xtensa::EPC5: + case Xtensa::EPC6: + case Xtensa::EPC7: + Res = STI.getFeatureBits()[Xtensa::FeatureHighPriInterrupts]; + Res = Res & (NumIntLevels >= (RegNo - Xtensa::EPC1)); + break; + case Xtensa::EPS2: + case Xtensa::EPS3: + case Xtensa::EPS4: + case Xtensa::EPS5: + case Xtensa::EPS6: + case Xtensa::EPS7: + Res = STI.getFeatureBits()[Xtensa::FeatureHighPriInterrupts]; + Res = Res & (NumIntLevels > (RegNo - Xtensa::EPS2)); + break; + case Xtensa::EXCSAVE1: + Res = STI.getFeatureBits()[Xtensa::FeatureException]; + break; + case Xtensa::EXCSAVE2: + case Xtensa::EXCSAVE3: + case Xtensa::EXCSAVE4: + case Xtensa::EXCSAVE5: + case Xtensa::EXCSAVE6: + case Xtensa::EXCSAVE7: + Res = STI.getFeatureBits()[Xtensa::FeatureHighPriInterrupts]; + Res = Res & (NumIntLevels >= (RegNo - Xtensa::EXCSAVE1)); + break; + case Xtensa::DEPC: + case Xtensa::EXCCAUSE: + case Xtensa::EXCVADDR: + Res = STI.getFeatureBits()[Xtensa::FeatureException]; + break; + case Xtensa::CPENABLE: + Res = STI.getFeatureBits()[Xtensa::FeatureCoprocessor]; + break; + case Xtensa::VECBASE: + Res = STI.getFeatureBits()[Xtensa::FeatureRelocatableVector]; + break; + case Xtensa::CCOUNT: + Res = STI.getFeatureBits()[Xtensa::FeatureTimerInt]; + Res &= (NumTimers > 0); + break; + case Xtensa::CCOMPARE0: + case Xtensa::CCOMPARE1: + case Xtensa::CCOMPARE2: + Res = STI.getFeatureBits()[Xtensa::FeatureTimerInt]; + Res &= (NumTimers > (RegNo - Xtensa::CCOMPARE0)); + break; + case Xtensa::PRID: + Res = STI.getFeatureBits()[Xtensa::FeaturePRID]; + break; + case Xtensa::INTSET: + case Xtensa::INTCLEAR: + case Xtensa::INTENABLE: + Res = STI.getFeatureBits()[Xtensa::FeatureInterrupt]; + break; + case Xtensa::MISC0: + case Xtensa::MISC1: + case Xtensa::MISC2: + case Xtensa::MISC3: + Res = STI.getFeatureBits()[Xtensa::FeatureMiscSR]; + Res &= (NumMiscSR > (RegNo - Xtensa::MISC0)); + break; + case Xtensa::THREADPTR: + Res = STI.getFeatureBits()[Xtensa::FeatureTHREADPTR]; + break; + case Xtensa::GPIO_OUT: + Res = IsESP32S2; + break; + case Xtensa::EXPSTATE: + Res = IsESP32; + break; + case Xtensa::FCR: + case Xtensa::FSR: + Res = STI.getFeatureBits()[Xtensa::FeatureSingleFloat]; + break; + case Xtensa::F64R_LO: + case Xtensa::F64R_HI: + case Xtensa::F64S: + Res = STI.getFeatureBits()[Xtensa::FeatureDFPAccel]; + break; + } + + return Res; +} + +static const unsigned SRDecoderTable[] = { + Xtensa::LBEG, 0, Xtensa::LEND, 1, + Xtensa::LCOUNT, 2, Xtensa::SAR, 3, + Xtensa::BREG, 4, Xtensa::LITBASE, 5, + Xtensa::SCOMPARE1, 12, Xtensa::ACCLO, 16, + Xtensa::ACCHI, 17, Xtensa::M0, 32, + Xtensa::M1, 33, Xtensa::M2, 34, + Xtensa::M3, 35, Xtensa::WINDOWBASE, 72, + Xtensa::WINDOWSTART, 73, Xtensa::IBREAKENABLE, 96, + Xtensa::MEMCTL, 97, Xtensa::ATOMCTL, 99, + Xtensa::DDR, 104, Xtensa::IBREAKA0, 128, + Xtensa::IBREAKA1, 129, Xtensa::DBREAKA0, 144, + Xtensa::DBREAKA1, 145, Xtensa::DBREAKC0, 160, + Xtensa::DBREAKC1, 161, Xtensa::CONFIGID0, 176, + Xtensa::EPC1, 177, Xtensa::EPC2, 178, + Xtensa::EPC3, 179, Xtensa::EPC4, 180, + Xtensa::EPC5, 181, Xtensa::EPC6, 182, + Xtensa::EPC7, 183, Xtensa::DEPC, 192, + Xtensa::EPS2, 194, Xtensa::EPS3, 195, + Xtensa::EPS4, 196, Xtensa::EPS5, 197, + Xtensa::EPS6, 198, Xtensa::EPS7, 199, + Xtensa::CONFIGID1, 208, Xtensa::EXCSAVE1, 209, + Xtensa::EXCSAVE2, 210, Xtensa::EXCSAVE3, 211, + Xtensa::EXCSAVE4, 212, Xtensa::EXCSAVE5, 213, + Xtensa::EXCSAVE6, 214, Xtensa::EXCSAVE7, 215, + Xtensa::CPENABLE, 224, Xtensa::INTSET, 226, + Xtensa::INTCLEAR, 227, Xtensa::INTENABLE, 228, + Xtensa::PS, 230, Xtensa::VECBASE, 231, + Xtensa::EXCCAUSE, 232, Xtensa::DEBUGCAUSE, 233, + Xtensa::CCOUNT, 234, Xtensa::PRID, 235, + Xtensa::ICOUNT, 236, Xtensa::ICOUNTLEVEL, 237, + Xtensa::EXCVADDR, 238, Xtensa::CCOMPARE0, 240, + Xtensa::CCOMPARE1, 241, Xtensa::CCOMPARE2, 242, + Xtensa::MISC0, 244, Xtensa::MISC1, 245, + Xtensa::MISC2, 246, Xtensa::MISC3, 247}; + +static DecodeStatus DecodeSRRegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + const llvm::MCSubtargetInfo STI = + ((const MCDisassembler *)Decoder)->getSubtargetInfo(); + + if (RegNo > 255) + return MCDisassembler::Fail; + + for (unsigned i = 0; i < array_lengthof(SRDecoderTable); i += 2) { + if (SRDecoderTable[i + 1] == RegNo) { + unsigned Reg = SRDecoderTable[i]; + + if (!CheckRegister(Reg, STI)) + return MCDisassembler::Fail; + + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; + } + } + + return MCDisassembler::Fail; +} + +static const unsigned URDecoderTable[] = { + Xtensa::GPIO_OUT, 0, Xtensa::EXPSTATE, 230, Xtensa::THREADPTR, 231, + Xtensa::FCR, 232, Xtensa::FSR, 233, Xtensa::F64R_LO, 234, + Xtensa::F64R_HI, 235, Xtensa::F64S, 236}; + +static DecodeStatus DecodeURRegisterClass(MCInst &Inst, uint64_t RegNo, + uint64_t Address, + const void *Decoder) { + const llvm::MCSubtargetInfo STI = + ((const MCDisassembler *)Decoder)->getSubtargetInfo(); + + if (RegNo > 255) + return MCDisassembler::Fail; + + for (unsigned i = 0; i < array_lengthof(URDecoderTable); i += 2) { + if (URDecoderTable[i + 1] == RegNo) { + unsigned Reg = URDecoderTable[i]; + + if (!CheckRegister(Reg, STI)) + return MCDisassembler::Fail; + + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; + } + } + + return MCDisassembler::Fail; +} + +static bool tryAddingSymbolicOperand(int64_t Value, bool isBranch, + uint64_t Address, uint64_t Offset, + uint64_t InstSize, MCInst &MI, + const void *Decoder) { + const MCDisassembler *Dis = static_cast(Decoder); + return Dis->tryAddingSymbolicOperand(MI, Value, Address, isBranch, Offset, /*OpSize=*/0, + InstSize); +} + +static DecodeStatus decodeCallOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<18>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm(SignExtend64<20>(Imm << 2))); + return MCDisassembler::Success; +} + +static DecodeStatus decodeJumpOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<18>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm(SignExtend64<18>(Imm))); + return MCDisassembler::Success; +} + +static DecodeStatus decodeBranchOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + switch (Inst.getOpcode()) { + case Xtensa::BEQZ: + case Xtensa::BGEZ: + case Xtensa::BLTZ: + case Xtensa::BNEZ: + assert(isUInt<12>(Imm) && "Invalid immediate"); + if (!tryAddingSymbolicOperand(SignExtend64<12>(Imm) + 4 + Address, true, + Address, 0, 3, Inst, Decoder)) + Inst.addOperand(MCOperand::createImm(SignExtend64<12>(Imm))); + break; + default: + assert(isUInt<8>(Imm) && "Invalid immediate"); + if (!tryAddingSymbolicOperand(SignExtend64<8>(Imm) + 4 + Address, true, + Address, 0, 3, Inst, Decoder)) + Inst.addOperand(MCOperand::createImm(SignExtend64<8>(Imm))); + } + return MCDisassembler::Success; +} + +static DecodeStatus decodeLoopOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + + assert(isUInt<8>(Imm) && "Invalid immediate"); + if (!tryAddingSymbolicOperand(Imm + 4 + Address, true, Address, 0, 3, Inst, + Decoder)) + Inst.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeL32ROperand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + + assert(isUInt<16>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm( + SignExtend64<17>((Imm << 2) + 0x40000 + (Address & 0x3)))); + return MCDisassembler::Success; +} + +static DecodeStatus decodeImm8Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<8>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm(SignExtend64<8>(Imm))); + return MCDisassembler::Success; +} + +static DecodeStatus decodeImm8_sh8Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, + const void *Decoder) { + assert(isUInt<8>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm(SignExtend64<16>(Imm << 8))); + return MCDisassembler::Success; +} + +static DecodeStatus decodeImm12Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<12>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm(SignExtend64<12>(Imm))); + return MCDisassembler::Success; +} + +static DecodeStatus decodeUimm4Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<4>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeUimm5Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<5>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeImm1_16Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<4>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm(Imm + 1)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeImm1n_15Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, + const void *Decoder) { + assert(isUInt<4>(Imm) && "Invalid immediate"); + if (!Imm) + Inst.addOperand(MCOperand::createImm(-1)); + else + Inst.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeImm32n_95Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, + const void *Decoder) { + assert(isUInt<7>(Imm) && "Invalid immediate"); + if ((Imm & 0x60) == 0x60) + Inst.addOperand(MCOperand::createImm((~0x1f) | Imm)); + else + Inst.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeImm8n_7Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<4>(Imm) && "Invalid immediate"); + if (Imm > 7) + Inst.addOperand(MCOperand::createImm(Imm - 16)); + else + Inst.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeImm64n_4nOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, + const void *Decoder) { + assert(isUInt<4>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm((~0x3f) | (Imm << 2))); + return MCDisassembler::Success; +} + +static DecodeStatus decodeEntry_Imm12OpValue(MCInst &Inst, uint64_t Imm, + int64_t Address, + const void *Decoder) { + assert(isUInt<12>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm(Imm << 3)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeShimm1_31Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, + const void *Decoder) { + assert(isUInt<5>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm(32 - Imm)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeSeimm7_22Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, + const void *Decoder) { + assert(isUInt<4>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm(Imm + 7)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeSelect_256Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, + const void *Decoder) { + assert(isUInt<8>(Imm) && "Invalid immediate"); + Inst.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + +static int64_t TableB4const[16] = {-1, 1, 2, 3, 4, 5, 6, 7, + 8, 10, 12, 16, 32, 64, 128, 256}; +static DecodeStatus decodeB4constOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<4>(Imm) && "Invalid immediate"); + + Inst.addOperand(MCOperand::createImm(TableB4const[Imm])); + return MCDisassembler::Success; +} + +static int64_t TableB4constu[16] = {32768, 65536, 2, 3, 4, 5, 6, 7, + 8, 10, 12, 16, 32, 64, 128, 256}; +static DecodeStatus decodeB4constuOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, + const void *Decoder) { + assert(isUInt<4>(Imm) && "Invalid immediate"); + + Inst.addOperand(MCOperand::createImm(TableB4constu[Imm])); + return MCDisassembler::Success; +} + +static DecodeStatus decodeMem8Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<12>(Imm) && "Invalid immediate"); + DecodeARRegisterClass(Inst, Imm & 0xf, Address, Decoder); + Inst.addOperand(MCOperand::createImm((Imm >> 4) & 0xff)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeMem16Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<12>(Imm) && "Invalid immediate"); + DecodeARRegisterClass(Inst, Imm & 0xf, Address, Decoder); + Inst.addOperand(MCOperand::createImm((Imm >> 3) & 0x1fe)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeMem32Operand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<12>(Imm) && "Invalid immediate"); + DecodeARRegisterClass(Inst, Imm & 0xf, Address, Decoder); + Inst.addOperand(MCOperand::createImm((Imm >> 2) & 0x3fc)); + return MCDisassembler::Success; +} + +static DecodeStatus decodeMem32nOperand(MCInst &Inst, uint64_t Imm, + int64_t Address, const void *Decoder) { + assert(isUInt<8>(Imm) && "Invalid immediate"); + DecodeARRegisterClass(Inst, Imm & 0xf, Address, Decoder); + Inst.addOperand(MCOperand::createImm((Imm >> 2) & 0x3c)); + return MCDisassembler::Success; +} + +/// Read two bytes from the ArrayRef and return 16 bit data sorted +/// according to the given endianness. +static DecodeStatus readInstruction16(ArrayRef Bytes, uint64_t Address, + uint64_t &Size, uint32_t &Insn, + bool IsLittleEndian) { + // We want to read exactly 2 Bytes of data. + if (Bytes.size() < 2) { + Size = 0; + return MCDisassembler::Fail; + } + + if (!IsLittleEndian) { + llvm_unreachable("Big-endian mode currently is not supported!"); + } else { + Insn = (Bytes[1] << 8) | Bytes[0]; + } + + return MCDisassembler::Success; +} + +/// Read four bytes from the ArrayRef and return 24 bit data sorted +/// according to the given endianness. +static DecodeStatus readInstruction24(ArrayRef Bytes, uint64_t Address, + uint64_t &Size, uint32_t &Insn, + bool IsLittleEndian) { + // We want to read exactly 3 Bytes of data. + if (Bytes.size() < 3) { + Size = 0; + return MCDisassembler::Fail; + } + + if (!IsLittleEndian) { + report_fatal_error("Big-endian mode currently is not supported!"); + } else { + Insn = (Bytes[2] << 16) | (Bytes[1] << 8) | (Bytes[0] << 0); + } + + return MCDisassembler::Success; +} + +#include "XtensaGenDisassemblerTables.inc" + +DecodeStatus XtensaDisassembler::getInstruction(MCInst &MI, uint64_t &Size, + ArrayRef Bytes, + uint64_t Address, + raw_ostream &CS) const { + uint32_t Insn; + DecodeStatus Result; + + if (hasDensity()) { + Result = readInstruction16(Bytes, Address, Size, Insn, IsLittleEndian); + if (Result == MCDisassembler::Fail) + return MCDisassembler::Fail; + LLVM_DEBUG(dbgs() << "Trying Xtensa 16-bit instruction table :\n"); + Result = decodeInstruction(DecoderTable16, MI, Insn, Address, this, STI); + if (Result != MCDisassembler::Fail) { + Size = 2; + return Result; + } + } + + Result = readInstruction24(Bytes, Address, Size, Insn, IsLittleEndian); + if (Result == MCDisassembler::Fail) + return MCDisassembler::Fail; + LLVM_DEBUG(dbgs() << "Trying Xtensa 24-bit instruction table :\n"); + Result = decodeInstruction(DecoderTable24, MI, Insn, Address, this, STI); + Size = 3; + return Result; +} diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/Xtensa/MCTargetDesc/CMakeLists.txt new file mode 100644 index 0000000000000..dc12863394c7a --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/CMakeLists.txt @@ -0,0 +1,18 @@ +add_llvm_component_library(LLVMXtensaDesc + XtensaAsmBackend.cpp + XtensaELFObjectWriter.cpp + XtensaInstPrinter.cpp + XtensaMCAsmInfo.cpp + XtensaMCCodeEmitter.cpp + XtensaMCExpr.cpp + XtensaMCTargetDesc.cpp + XtensaTargetStreamer.cpp + + LINK_COMPONENTS + MC + Support + XtensaInfo + + ADD_TO_COMPONENT + Xtensa + ) diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaAsmBackend.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaAsmBackend.cpp new file mode 100644 index 0000000000000..6981c82e768f5 --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaAsmBackend.cpp @@ -0,0 +1,228 @@ +//===-- XtensaMCAsmBackend.cpp - Xtensa assembler backend -----------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/XtensaFixupKinds.h" +#include "MCTargetDesc/XtensaMCTargetDesc.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace llvm { +class MCObjectTargetWriter; +class XtensaMCAsmBackend : public MCAsmBackend { + uint8_t OSABI; + bool IsLittleEndian; + +public: + XtensaMCAsmBackend(uint8_t osABI, bool isLE) + : MCAsmBackend(support::little), OSABI(osABI), IsLittleEndian(isLE) {} + + unsigned getNumFixupKinds() const override { + return Xtensa::NumTargetFixupKinds; + } + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, MutableArrayRef Data, + uint64_t Value, bool IsResolved, + const MCSubtargetInfo *STI) const override; + bool mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const override; + bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, + const MCRelaxableFragment *Fragment, + const MCAsmLayout &Layout) const override; + void relaxInstruction(MCInst &Inst, + const MCSubtargetInfo &STI) const override; + bool writeNopData(raw_ostream &OS, uint64_t Count, + const MCSubtargetInfo *STI) const override; + + std::unique_ptr createObjectTargetWriter() const override { + return createXtensaObjectWriter(OSABI, IsLittleEndian); + } +}; +} // namespace llvm + +const MCFixupKindInfo & +XtensaMCAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { + const static MCFixupKindInfo Infos[Xtensa::NumTargetFixupKinds] = { + // name offset bits flags + {"fixup_xtensa_branch_6", 0, 16, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_xtensa_branch_8", 16, 8, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_xtensa_branch_12", 12, 12, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_xtensa_jump_18", 6, 18, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_xtensa_call_18", 6, 18, + MCFixupKindInfo::FKF_IsPCRel | + MCFixupKindInfo::FKF_IsAlignedDownTo32Bits}, + {"fixup_xtensa_l32r_16", 8, 16, + MCFixupKindInfo::FKF_IsPCRel | + MCFixupKindInfo::FKF_IsAlignedDownTo32Bits}, + {"fixup_xtensa_loop_8", 16, 8, MCFixupKindInfo::FKF_IsPCRel}}; + + if (Kind < FirstTargetFixupKind) + return MCAsmBackend::getFixupKindInfo(Kind); + assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && + "Invalid kind!"); + return Infos[Kind - FirstTargetFixupKind]; +} + +static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, + MCContext &Ctx) { + unsigned Kind = Fixup.getKind(); + switch (Kind) { + default: + llvm_unreachable("Unknown fixup kind!"); + case FK_Data_1: + case FK_Data_2: + case FK_Data_4: + case FK_Data_8: + return Value; + case Xtensa::fixup_xtensa_branch_6: { + Value -= 4; + if (!isInt<6>(Value)) + Ctx.reportError(Fixup.getLoc(), "branch 6-bit fixup value out is of range"); + unsigned Hi2 = (Value >> 4) & 0x3; + unsigned Lo4 = Value & 0xf; + return (Hi2 << 4) | (Lo4 << 12); + } + case Xtensa::fixup_xtensa_branch_8: + Value -= 4; + if (!isInt<8>(Value)) + Ctx.reportError(Fixup.getLoc(), "branch 8-bit fixup value out of range"); + return (Value & 0xff); + case Xtensa::fixup_xtensa_branch_12: + Value -= 4; + if (!isInt<12>(Value)) + Ctx.reportError(Fixup.getLoc(), "branch 12-bit fixup value out of range"); + return (Value & 0xfff); + case Xtensa::fixup_xtensa_jump_18: + Value -= 4; + if (!isInt<18>(Value)) + Ctx.reportError(Fixup.getLoc(), "jump fixup value out of range"); + return (Value & 0x3ffff); + case Xtensa::fixup_xtensa_call_18: + Value -= 4; + if (!isInt<20>(Value)) + Ctx.reportError(Fixup.getLoc(), "call fixup value out of range"); + if (Value & 0x3) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned"); + return (Value & 0xffffc) >> 2; + case Xtensa::fixup_xtensa_loop_8: + Value -= 4; + if (!isUInt<8>(Value)) + Ctx.reportError(Fixup.getLoc(), "loop fixup value out of range"); + return (Value & 0xff); + case Xtensa::fixup_xtensa_l32r_16: + unsigned Offset = Fixup.getOffset(); + if (Offset & 0x3) + Value -= 4; + if (!isInt<18>(Value) && (Value & 0x20000)) + Ctx.reportError(Fixup.getLoc(), "l32r fixup value out of range"); + if (Value & 0x3) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 4-byte aligned"); + return (Value & 0x3fffc) >> 2; + } +} + +static unsigned getSize(unsigned Kind) { + switch (Kind) { + default: + return 3; + case MCFixupKind::FK_Data_4: + return 4; + case Xtensa::fixup_xtensa_branch_6: + return 2; + } +} + +void XtensaMCAsmBackend::applyFixup(const MCAssembler &Asm, + const MCFixup &Fixup, const MCValue &Target, + MutableArrayRef Data, uint64_t Value, + bool IsResolved, + const MCSubtargetInfo *STI) const { + MCContext &Ctx = Asm.getContext(); + MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind()); + + Value = adjustFixupValue(Fixup, Value, Ctx); + + // Shift the value into position. + Value <<= Info.TargetOffset; + + if (!Value) + return; // Doesn't change encoding. + + unsigned Offset = Fixup.getOffset(); + unsigned FullSize = getSize(Fixup.getKind()); + + for (unsigned i = 0; i != FullSize; ++i) { + Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff); + } +} + +bool XtensaMCAsmBackend::mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const { + return false; +} + +bool XtensaMCAsmBackend::fixupNeedsRelaxation( + const MCFixup &Fixup, uint64_t Value, const MCRelaxableFragment *Fragment, + const MCAsmLayout &Layout) const { + return false; +} + +void XtensaMCAsmBackend::relaxInstruction(MCInst &Inst, + const MCSubtargetInfo &STI) const {} + +bool XtensaMCAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count, + const MCSubtargetInfo *STI) const { + uint64_t NumNops24b = Count / 3; + + for (uint64_t i = 0; i != NumNops24b; ++i) { + // Currently just little-endian machine supported, + // but probably big-endian will be also implemented in future + if (IsLittleEndian) { + OS.write("\xf0", 1); + OS.write("\x20", 1); + OS.write("\0x00", 1); + } else { + report_fatal_error("Big-endian mode currently is not supported!"); + } + Count -= 3; + } + + // TODO maybe function should return error if (Count > 0) + switch (Count) { + default: + break; + case 1: + OS.write("\0", 1); + break; + case 2: + OS.write("\0\0", 2); + break; + } + + return true; +} + +MCAsmBackend *llvm::createXtensaMCAsmBackend(const Target &T, + const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options) { + uint8_t OSABI = + MCELFObjectTargetWriter::getOSABI(STI.getTargetTriple().getOS()); + return new llvm::XtensaMCAsmBackend(OSABI, true); +} diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaELFObjectWriter.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaELFObjectWriter.cpp new file mode 100644 index 0000000000000..439f0d7041de4 --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaELFObjectWriter.cpp @@ -0,0 +1,70 @@ +//===-- XtensaMCObjectWriter.cpp - Xtensa ELF writer ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/XtensaMCTargetDesc.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/ErrorHandling.h" +#include +#include + +using namespace llvm; + +namespace { +class XtensaObjectWriter : public MCELFObjectTargetWriter { +public: + XtensaObjectWriter(uint8_t OSABI); + + virtual ~XtensaObjectWriter(); + +protected: + unsigned getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, bool IsPCRel) const override; + bool needsRelocateWithSymbol(const MCSymbol &Sym, + unsigned Type) const override; +}; +} // namespace + +XtensaObjectWriter::XtensaObjectWriter(uint8_t OSABI) + : MCELFObjectTargetWriter(false, OSABI, ELF::EM_XTENSA, + /*HasRelocationAddend=*/true) {} + +XtensaObjectWriter::~XtensaObjectWriter() {} + +unsigned XtensaObjectWriter::getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, + bool IsPCRel) const { + MCSymbolRefExpr::VariantKind Modifier = Target.getAccessVariant(); + + switch ((unsigned)Fixup.getKind()) { + case FK_Data_4: + if (Modifier == MCSymbolRefExpr::VariantKind::VK_TPOFF) + return ELF::R_XTENSA_TLS_TPOFF; + else + return ELF::R_XTENSA_32; + default: + return ELF::R_XTENSA_SLOT0_OP; + } +} + +std::unique_ptr +llvm::createXtensaObjectWriter(uint8_t OSABI, bool IsLittleEndian) { + return std::make_unique(OSABI); +} + +bool XtensaObjectWriter::needsRelocateWithSymbol(const MCSymbol &Sym, + unsigned Type) const { + return false; +} diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaFixupKinds.h b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaFixupKinds.h new file mode 100644 index 0000000000000..1ef683a934b5b --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaFixupKinds.h @@ -0,0 +1,33 @@ +//===-- XtensaMCFixups.h - Xtensa-specific fixup entries --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_MCTARGETDESC_XTENSAMCFIXUPS_H +#define LLVM_LIB_TARGET_XTENSA_MCTARGETDESC_XTENSAMCFIXUPS_H + +#include "llvm/MC/MCFixup.h" + +namespace llvm { +namespace Xtensa { +enum FixupKind { + fixup_xtensa_branch_6 = FirstTargetFixupKind, + fixup_xtensa_branch_8, + fixup_xtensa_branch_12, + fixup_xtensa_jump_18, + fixup_xtensa_call_18, + fixup_xtensa_l32r_16, + fixup_xtensa_loop_8, + fixup_xtensa_invalid, + LastTargetFixupKind, + NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind +}; +} // end namespace Xtensa +} // end namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_MCTARGETDESC_XTENSAMCFIXUPS_H */ diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.cpp new file mode 100644 index 0000000000000..df5d67501de74 --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.cpp @@ -0,0 +1,441 @@ +//===- XtensaInstPrinter.cpp - Convert Xtensa MCInst to asm syntax --------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This class prints an Xtensa MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#include "XtensaInstPrinter.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +#include "XtensaGenAsmWriter.inc" + +static void printExpr(const MCExpr *Expr, raw_ostream &OS) { + int Offset = 0; + const MCSymbolRefExpr *SRE; + + if (!(SRE = dyn_cast(Expr))) + assert(false && "Unexpected MCExpr type."); + + MCSymbolRefExpr::VariantKind Kind = SRE->getKind(); + + switch (Kind) { + case MCSymbolRefExpr::VK_None: + break; + // TODO + default: + report_fatal_error("Invalid kind!"); + } + + OS << SRE->getSymbol(); + + if (Offset) { + if (Offset > 0) + OS << '+'; + OS << Offset; + } + + if (Kind != MCSymbolRefExpr::VK_None) + OS << ')'; +} + +void XtensaInstPrinter::printOperand(const MCOperand &MC, raw_ostream &O) { + if (MC.isReg()) + O << getRegisterName(MC.getReg()); + else if (MC.isImm()) + O << MC.getImm(); + else if (MC.isExpr()) + printExpr(MC.getExpr(), O); + else + report_fatal_error("Invalid operand"); +} + +void XtensaInstPrinter::printInst(const MCInst *MI, uint64_t Address, + StringRef Annot, const MCSubtargetInfo &STI, + raw_ostream &O) { + printInstruction(MI, Address, O); + printAnnotation(O, Annot); +} + +void XtensaInstPrinter::printRegName(raw_ostream &O, unsigned RegNo) const { + O << getRegisterName(RegNo); +} + +void XtensaInstPrinter::printOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + printOperand(MI->getOperand(OpNum), O); +} + +void XtensaInstPrinter::printMemOperand(const MCInst *MI, int OpNum, + raw_ostream &OS) { + OS << getRegisterName(MI->getOperand(OpNum).getReg()); + OS << ", "; + printOperand(MI, OpNum + 1, OS); +} + +void XtensaInstPrinter::printBranchTarget(const MCInst *MI, int OpNum, + raw_ostream &OS) { + const MCOperand &MC = MI->getOperand(OpNum); + if (MI->getOperand(OpNum).isImm()) { + int64_t Val = MC.getImm() + 4; + OS << ". "; + if (Val > 0) + OS << '+'; + OS << Val; + } else if (MC.isExpr()) + MC.getExpr()->print(OS, &MAI, true); + else + llvm_unreachable("Invalid operand"); +} + +void XtensaInstPrinter::printLoopTarget(const MCInst *MI, int OpNum, + raw_ostream &OS) { + const MCOperand &MC = MI->getOperand(OpNum); + if (MI->getOperand(OpNum).isImm()) { + int64_t Val = MC.getImm() + 4; + OS << ". "; + if (Val > 0) + OS << '+'; + OS << Val; + } else if (MC.isExpr()) + MC.getExpr()->print(OS, &MAI, true); + else + llvm_unreachable("Invalid operand"); +} + +void XtensaInstPrinter::printJumpTarget(const MCInst *MI, int OpNum, + raw_ostream &OS) { + const MCOperand &MC = MI->getOperand(OpNum); + if (MC.isImm()) { + int64_t Val = MC.getImm() + 4; + OS << ". "; + if (Val > 0) + OS << '+'; + OS << Val; + } else if (MC.isExpr()) + MC.getExpr()->print(OS, &MAI, true); + else + llvm_unreachable("Invalid operand"); + ; +} + +void XtensaInstPrinter::printCallOperand(const MCInst *MI, int OpNum, + raw_ostream &OS) { + const MCOperand &MC = MI->getOperand(OpNum); + if (MC.isImm()) { + int64_t Val = MC.getImm() + 4; + OS << ". "; + if (Val > 0) + OS << '+'; + OS << Val; + } else if (MC.isExpr()) + MC.getExpr()->print(OS, &MAI, true); + else + llvm_unreachable("Invalid operand"); +} + +void XtensaInstPrinter::printL32RTarget(const MCInst *MI, int OpNum, + raw_ostream &O) { + const MCOperand &MC = MI->getOperand(OpNum); + if (MC.isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + int64_t InstrOff = Value & 0x3; + Value -= InstrOff; + assert((Value >= -262144 && Value <= -4) && + "Invalid argument, value must be in ranges [-262144,-4]"); + Value += ((InstrOff + 0x3) & 0x4) - InstrOff; + O << ". "; + O << Value; + } else if (MC.isExpr()) + MC.getExpr()->print(O, &MAI, true); + else + llvm_unreachable("Invalid operand"); +} + +void XtensaInstPrinter::printImm8_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= -128 && Value <= 127) && + "Invalid argument, value must be in ranges [-128,127]"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printImm8_sh8_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= -32768 && Value <= 32512 && ((Value & 0xFF) == 0)) && + "Invalid argument, value must be multiples of 256 in range " + "[-32768,32512]"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printImm12_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= -2048 && Value <= 2047) && + "Invalid argument, value must be in ranges [-2048,2047]"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printImm12m_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= -2048 && Value <= 2047) && + "Invalid argument, value must be in ranges [-2048,2047]"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printUimm4_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= 0 && Value <= 15) && "Invalid argument"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printUimm5_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= 0 && Value <= 31) && "Invalid argument"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printShimm1_31_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= 1 && Value <= 31) && + "Invalid argument, value must be in range [1,31]"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printImm1_16_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= 1 && Value <= 16) && + "Invalid argument, value must be in range [1,16]"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printImm1n_15_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= -1 && (Value != 0) && Value <= 15) && + "Invalid argument, value must be in ranges <-1,-1> or <1,15>"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printImm32n_95_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= -32 && Value <= 95) && + "Invalid argument, value must be in ranges <-32,95>"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printImm8n_7_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= -8 && Value <= 7) && + "Invalid argument, value must be in ranges <-8,7>"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printImm64n_4n_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= -64 && Value <= -4) & ((Value & 0x3) == 0) && + "Invalid argument, value must be in ranges <-64,-4>"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printOffset8m8_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= 0 && Value <= 255) && + "Invalid argument, value must be in range [0,255]"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printOffset8m16_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= 0 && Value <= 510 && ((Value & 0x1) == 0)) && + "Invalid argument, value must be multiples of two in range [0,510]"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printOffset8m32_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert( + (Value >= 0 && Value <= 1020 && ((Value & 0x3) == 0)) && + "Invalid argument, value must be multiples of four in range [0,1020]"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printOffset4m32_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= 0 && Value <= 60 && ((Value & 0x3) == 0)) && + "Invalid argument, value must be multiples of four in range [0,60]"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printEntry_Imm12_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= 0 && Value <= 32760) && + "Invalid argument, value must be multiples of eight in range " + "<0,32760>"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printB4const_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + + switch (Value) { + case -1: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 10: + case 12: + case 16: + case 32: + case 64: + case 128: + case 256: + break; + default: + assert((0) && "Invalid B4const argument"); + } + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printB4constu_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + + switch (Value) { + case 32768: + case 65536: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 10: + case 12: + case 16: + case 32: + case 64: + case 128: + case 256: + break; + default: + assert((0) && "Invalid B4constu argument"); + } + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printSeimm7_22_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= 7 && Value <= 22) && + "Invalid argument, value must be in range <7,22>"); + O << Value; + } else + printOperand(MI, OpNum, O); +} + +void XtensaInstPrinter::printSelect_256_AsmOperand(const MCInst *MI, int OpNum, + raw_ostream &O) { + if (MI->getOperand(OpNum).isImm()) { + int64_t Value = MI->getOperand(OpNum).getImm(); + assert((Value >= 0 && Value <= 255) && + "Invalid argument, value must be in range [0,255]"); + O << Value; + } else + printOperand(MI, OpNum, O); +} diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.h b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.h new file mode 100644 index 0000000000000..8530c2560968b --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.h @@ -0,0 +1,77 @@ +//===- XtensaInstPrinter.h - Convert Xtensa MCInst to asm syntax -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This class prints an Xtensa MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_MCTARGETDESC_XTENSAINSTPRINTER_H +#define LLVM_LIB_TARGET_XTENSA_MCTARGETDESC_XTENSAINSTPRINTER_H + +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/Support/Compiler.h" + +namespace llvm { +class MCOperand; + +class XtensaInstPrinter : public MCInstPrinter { +public: + XtensaInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : MCInstPrinter(MAI, MII, MRI) {} + + // Automatically generated by tblgen. + std::pair getMnemonic(const MCInst *MI) override; + void printInstruction(const MCInst *MI, uint64_t Address, raw_ostream &O); + static const char *getRegisterName(unsigned RegNo); + + // Print the given operand. + static void printOperand(const MCOperand &MO, raw_ostream &O); + + // Override MCInstPrinter. + void printRegName(raw_ostream &O, unsigned RegNo) const override; + void printInst(const MCInst *MI, uint64_t Address, StringRef Annot, + const MCSubtargetInfo &STI, raw_ostream &O) override; + +private: + // Print various types of operand. + void printOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printMemOperand(const MCInst *MI, int OpNUm, raw_ostream &O); + void printBranchTarget(const MCInst *MI, int OpNum, raw_ostream &O); + void printLoopTarget(const MCInst *MI, int OpNum, raw_ostream &O); + void printJumpTarget(const MCInst *MI, int OpNum, raw_ostream &O); + void printCallOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printL32RTarget(const MCInst *MI, int OpNum, raw_ostream &O); + + void printImm8_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printImm8_sh8_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printImm12_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printImm12m_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printUimm4_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printUimm5_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printShimm1_31_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printImm1_16_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printImm1n_15_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printImm32n_95_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printImm8n_7_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printImm64n_4n_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printOffset8m8_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printOffset8m16_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printOffset8m32_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printOffset4m32_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printEntry_Imm12_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printB4const_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printB4constu_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printSeimm7_22_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); + void printSelect_256_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O); +}; +} // end namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_MCTARGETDESC_XTENSAINSTPRINTER_H */ diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCAsmInfo.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCAsmInfo.cpp new file mode 100644 index 0000000000000..53e92aba4e2ba --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCAsmInfo.cpp @@ -0,0 +1,34 @@ +//===-- XtensaMCAsmInfo.cpp - Xtensa Asm Properties -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the declarations of the XtensaMCAsmInfo properties. +// +//===----------------------------------------------------------------------===// + +#include "XtensaMCAsmInfo.h" +#include "llvm/ADT/Triple.h" + +using namespace llvm; + +XtensaMCAsmInfo::XtensaMCAsmInfo(const Triple &TT) { + CodePointerSize = 4; + CalleeSaveStackSlotSize = 4; + PrivateGlobalPrefix = ".L"; + CommentString = "#"; + ZeroDirective = "\t.space\t"; + Data16bitsDirective = "\t.half\t"; + Data32bitsDirective = "\t.word\t"; + Data64bitsDirective = "\t.quad\t"; + GlobalDirective = "\t.global\t"; + UsesELFSectionDirectiveForBSS = true; + SupportsDebugInformation = true; + ExceptionsType = ExceptionHandling::DwarfCFI; + AlignmentIsInBytes = true; +} diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCAsmInfo.h b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCAsmInfo.h new file mode 100644 index 0000000000000..921ccc88b827a --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCAsmInfo.h @@ -0,0 +1,30 @@ +//===-- XtensaMCAsmInfo.h - Xtensa Asm Info --------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the declaration of the XtensaMCAsmInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_MCTARGETDESC_XTENSATARGETASMINFO_H +#define LLVM_LIB_TARGET_XTENSA_MCTARGETDESC_XTENSATARGETASMINFO_H + +#include "llvm/MC/MCAsmInfoELF.h" + +namespace llvm { +class Triple; + +class XtensaMCAsmInfo : public MCAsmInfoELF { +public: + explicit XtensaMCAsmInfo(const Triple &TT); +}; + +} // namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_MCTARGETDESC_XTENSATARGETASMINFO_H */ diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCCodeEmitter.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCCodeEmitter.cpp new file mode 100644 index 0000000000000..35a016eff25cd --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCCodeEmitter.cpp @@ -0,0 +1,612 @@ +//===-- XtensaMCCodeEmitter.cpp - Convert Xtensa Code to Machine Code -----===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the XtensaMCCodeEmitter class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/XtensaFixupKinds.h" +#include "MCTargetDesc/XtensaMCExpr.h" +#include "MCTargetDesc/XtensaMCTargetDesc.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" + +#define GET_INSTRMAP_INFO +#include "XtensaGenInstrInfo.inc" +#undef GET_INSTRMAP_INFO + +using namespace llvm; + +#define DEBUG_TYPE "mccodeemitter" + +namespace { +class XtensaMCCodeEmitter : public MCCodeEmitter { + const MCInstrInfo &MCII; + MCContext &Ctx; + bool IsLittleEndian; + +public: + XtensaMCCodeEmitter(const MCInstrInfo &mcii, MCContext &ctx, bool isLE) + : MCII(mcii), Ctx(ctx), IsLittleEndian(isLE) {} + + ~XtensaMCCodeEmitter() {} + + // OVerride MCCodeEmitter. + void encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const override; + +private: + // Automatically generated by TableGen. + uint64_t getBinaryCodeForInstr(const MCInst &MI, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // Called by the TableGen code to get the binary encoding of operand + // MO in MI. Fixups is the list of fixups against MI. + uint32_t getMachineOpValue(const MCInst &MI, const MCOperand &MO, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getJumpTargetEncoding(const MCInst &MI, unsigned int OpNum, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getBranchTargetEncoding(const MCInst &MI, unsigned int OpNum, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getLoopTargetEncoding(const MCInst &MI, unsigned int OpNum, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getCallEncoding(const MCInst &MI, unsigned int OpNum, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getL32RTargetEncoding(const MCInst &MI, unsigned OpNum, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getMemRegEncoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getImm8OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getImm8_sh8OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getImm12OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getUimm4OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getUimm5OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getImm1_16OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getImm1n_15OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getImm32n_95OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getImm8n_7OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getImm64n_4nOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getEntry_Imm12OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getShimm1_31OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getB4constOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getB4constuOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getSeimm7_22OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + uint32_t getSelect_256OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + +}; +} // namespace + +MCCodeEmitter *llvm::createXtensaMCCodeEmitter(const MCInstrInfo &MCII, + MCContext &Ctx) { + return new XtensaMCCodeEmitter(MCII, Ctx, true); +} + +void XtensaMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + uint64_t Bits = getBinaryCodeForInstr(MI, Fixups, STI); + unsigned Size = MCII.get(MI.getOpcode()).getSize(); + + if (IsLittleEndian) { + // Little-endian insertion of Size bytes. + unsigned ShiftValue = 0; + for (unsigned I = 0; I != Size; ++I) { + OS << uint8_t(Bits >> ShiftValue); + ShiftValue += 8; + } + } else { + // TODO Big-endian insertion of Size bytes. + report_fatal_error("Big-endian mode currently is not supported!"); + } +} + +uint32_t +XtensaMCCodeEmitter::getMachineOpValue(const MCInst &MI, const MCOperand &MO, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + if (MO.isReg()) + return Ctx.getRegisterInfo()->getEncodingValue(MO.getReg()); + if (MO.isImm()) { + uint32_t Res = static_cast(MO.getImm()); + return Res; + } + + report_fatal_error("Unhandled expression!"); + return 0; +} + +uint32_t +XtensaMCCodeEmitter::getJumpTargetEncoding(const MCInst &MI, unsigned int OpNum, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNum); + + if (MO.isImm()) + return MO.getImm(); + + const MCExpr *Expr = MO.getExpr(); + Fixups.push_back(MCFixup::create( + 0, Expr, MCFixupKind(Xtensa::fixup_xtensa_jump_18), MI.getLoc())); + return 0; +} + +uint32_t XtensaMCCodeEmitter::getBranchTargetEncoding( + const MCInst &MI, unsigned int OpNum, SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNum); + if (MO.isImm()) + return static_cast(MO.getImm()); + + const MCExpr *Expr = MO.getExpr(); + switch (MI.getOpcode()) { + case Xtensa::BEQZ: + case Xtensa::BGEZ: + case Xtensa::BLTZ: + case Xtensa::BNEZ: + Fixups.push_back(MCFixup::create( + 0, Expr, MCFixupKind(Xtensa::fixup_xtensa_branch_12), MI.getLoc())); + return 0; + default: + Fixups.push_back(MCFixup::create( + 0, Expr, MCFixupKind(Xtensa::fixup_xtensa_branch_8), MI.getLoc())); + return 0; + } +} + +uint32_t +XtensaMCCodeEmitter::getLoopTargetEncoding(const MCInst &MI, unsigned int OpNum, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNum); + if (MO.isImm()) + return static_cast(MO.getImm()); + + assert((MO.isExpr()) && "Unexpected operand value!"); + + const MCExpr *Expr = MO.getExpr(); + + Fixups.push_back(MCFixup::create( + 0, Expr, MCFixupKind(Xtensa::fixup_xtensa_loop_8), MI.getLoc())); + return 0; +} + +uint32_t +XtensaMCCodeEmitter::getCallEncoding(const MCInst &MI, unsigned int OpNum, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNum); + if (MO.isImm()) { + int32_t Res = MO.getImm(); + if (Res & 0x3) { + llvm_unreachable("Unexpected operand value!"); + } + Res >>= 2; + return Res; + } + + assert((MO.isExpr()) && "Unexpected operand value!"); + const MCExpr *Expr = MO.getExpr(); + Fixups.push_back(MCFixup::create( + 0, Expr, MCFixupKind(Xtensa::fixup_xtensa_call_18), MI.getLoc())); + return 0; +} + +uint32_t +XtensaMCCodeEmitter::getL32RTargetEncoding(const MCInst &MI, unsigned OpNum, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNum); + if (MO.isImm()) { + int32_t Res = MO.getImm(); + // We don't check first 2 bits, because in these bits we could store first 2 + // bits of instruction address + Res >>= 2; + return Res; + } + + assert((MO.isExpr()) && "Unexpected operand value!"); + + Fixups.push_back(MCFixup::create( + 0, MO.getExpr(), MCFixupKind(Xtensa::fixup_xtensa_l32r_16), MI.getLoc())); + return 0; +} + +uint32_t +XtensaMCCodeEmitter::getMemRegEncoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + assert(MI.getOperand(OpNo + 1).isImm()); + + uint32_t Res = static_cast(MI.getOperand(OpNo + 1).getImm()); + + switch (MI.getOpcode()) { + case Xtensa::S16I: + case Xtensa::L16SI: + case Xtensa::L16UI: + if (Res & 0x1) { + report_fatal_error("Unexpected operand value!"); + } + Res >>= 1; + break; + case Xtensa::S32I: + case Xtensa::L32I: + case Xtensa::S32I_N: + case Xtensa::L32I_N: + case Xtensa::SSI: + case Xtensa::SSIP: + case Xtensa::LSI: + case Xtensa::LSIP: + case Xtensa::S32C1I: + if (Res & 0x3) { + report_fatal_error("Unexpected operand value!"); + } + Res >>= 2; + break; + } + + switch (MI.getOpcode()) { + case Xtensa::S32I_N: + case Xtensa::L32I_N: + assert((isUInt<4>(Res)) && "Unexpected operand value!"); + break; + default: + assert((isUInt<8>(Res)) && "Unexpected operand value!"); + break; + } + + uint32_t OffBits = Res << 4; + uint32_t RegBits = getMachineOpValue(MI, MI.getOperand(OpNo), Fixups, STI); + + return ((OffBits & 0xFF0) | RegBits); +} + +uint32_t XtensaMCCodeEmitter::getImm8OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + int32_t Res = MO.getImm(); + + assert(((Res >= -128) && (Res <= 127)) && "Unexpected operand value!"); + + return (Res & 0xff); +} + +uint32_t +XtensaMCCodeEmitter::getImm8_sh8OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + int32_t Res = MO.getImm(); + + assert(((Res >= -32768) && (Res <= 32512) && ((Res & 0xff) == 0)) && + "Unexpected operand value!"); + + return (Res & 0xffff); +} + +uint32_t +XtensaMCCodeEmitter::getImm12OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + int32_t Res = MO.getImm(); + + assert(((Res >= -2048) && (Res <= 2047)) && "Unexpected operand value!"); + + return (Res & 0xfff); +} + +uint32_t +XtensaMCCodeEmitter::getUimm4OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + uint32_t Res = static_cast(MO.getImm()); + + assert((Res <= 15) && "Unexpected operand value!"); + + return Res & 0xf; +} + +uint32_t +XtensaMCCodeEmitter::getUimm5OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + uint32_t Res = static_cast(MO.getImm()); + + assert((Res <= 31) && "Unexpected operand value!"); + + return (Res & 0x1f); +} + +uint32_t +XtensaMCCodeEmitter::getShimm1_31OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + uint32_t Res = static_cast(MO.getImm()); + + assert(((Res >= 1) && (Res <= 31)) && "Unexpected operand value!"); + + return ((32 - Res) & 0x1f); +} + +uint32_t +XtensaMCCodeEmitter::getImm1_16OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + uint32_t Res = static_cast(MO.getImm()); + + assert(((Res >= 1) && (Res <= 16)) && "Unexpected operand value!"); + + return (Res - 1); +} + +uint32_t +XtensaMCCodeEmitter::getImm1n_15OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + int32_t Res = static_cast(MO.getImm()); + + assert(((Res >= -1) && (Res <= 15) && (Res != 0)) && + "Unexpected operand value!"); + + if (Res < 0) + Res = 0; + + return Res; +} + +uint32_t +XtensaMCCodeEmitter::getImm32n_95OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + int32_t Res = static_cast(MO.getImm()); + + assert(((Res >= -32) && (Res <= 95)) && "Unexpected operand value!"); + + return Res; +} + +uint32_t +XtensaMCCodeEmitter::getImm8n_7OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + int32_t Res = static_cast(MO.getImm()); + + assert(((Res >= -8) && (Res <= 7)) && "Unexpected operand value!"); + + if (Res < 0) + return Res + 16; + + return Res; +} + +uint32_t +XtensaMCCodeEmitter::getImm64n_4nOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + int32_t Res = static_cast(MO.getImm()); + + assert(((Res >= -64) && (Res <= -4) && ((Res & 0x3) == 0)) && + "Unexpected operand value!"); + + return Res & 0x3f; +} + +uint32_t +XtensaMCCodeEmitter::getEntry_Imm12OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + uint32_t res = static_cast(MO.getImm()); + + assert(((res & 0x7) == 0) && "Unexpected operand value!"); + + return res; +} + +uint32_t +XtensaMCCodeEmitter::getB4constOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + uint32_t Res = static_cast(MO.getImm()); + + switch (Res) { + case 0xffffffff: + Res = 0; + break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + break; + case 10: + Res = 9; + break; + case 12: + Res = 10; + break; + case 16: + Res = 11; + break; + case 32: + Res = 12; + break; + case 64: + Res = 13; + break; + case 128: + Res = 14; + break; + case 256: + Res = 15; + break; + default: + llvm_unreachable("Unexpected operand value!"); + } + + return Res; +} + +uint32_t +XtensaMCCodeEmitter::getB4constuOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + uint32_t Res = static_cast(MO.getImm()); + + switch (Res) { + case 32768: + Res = 0; + break; + case 65536: + Res = 1; + break; + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + break; + case 10: + Res = 9; + break; + case 12: + Res = 10; + break; + case 16: + Res = 11; + break; + case 32: + Res = 12; + break; + case 64: + Res = 13; + break; + case 128: + Res = 14; + break; + case 256: + Res = 15; + break; + default: + llvm_unreachable("Unexpected operand value!"); + } + + return Res; +} + +uint32_t +XtensaMCCodeEmitter::getSeimm7_22OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + uint32_t res = static_cast(MO.getImm()); + + res -= 7; + assert(((res & 0xf) == res) && "Unexpected operand value!"); + + return res; +} + +uint32_t +XtensaMCCodeEmitter::getSelect_256OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + uint32_t Res = static_cast(MO.getImm()); + + assert(((Res >= 0) && (Res <= 255)) && "Unexpected operand value!"); + + return Res; +} + +#include "XtensaGenMCCodeEmitter.inc" diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCExpr.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCExpr.cpp new file mode 100644 index 0000000000000..cafd8b7e29782 --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCExpr.cpp @@ -0,0 +1,63 @@ +//===-- XtensaMCExpr.cpp - Xtensa specific MC expression classes ----------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the implementation of the assembly expression modifiers +// accepted by the Xtensa architecture +// +//===----------------------------------------------------------------------===// + +#include "XtensaMCExpr.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; + +#define DEBUG_TYPE "xtensamcexpr" + +const XtensaMCExpr *XtensaMCExpr::create(const MCExpr *Expr, VariantKind Kind, + MCContext &Ctx) { + return new (Ctx) XtensaMCExpr(Expr, Kind); +} + +void XtensaMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { + bool HasVariant = getKind() != VK_Xtensa_None; + if (HasVariant) + OS << '%' << getVariantKindName(getKind()) << '('; + Expr->print(OS, MAI); + if (HasVariant) + OS << ')'; +} + +bool XtensaMCExpr::evaluateAsRelocatableImpl(MCValue &Res, + const MCAsmLayout *Layout, + const MCFixup *Fixup) const { + return getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup); +} + +void XtensaMCExpr::visitUsedExpr(MCStreamer &Streamer) const { + Streamer.visitUsedExpr(*getSubExpr()); +} + +XtensaMCExpr::VariantKind XtensaMCExpr::getVariantKindForName(StringRef name) { + return StringSwitch(name).Default( + VK_Xtensa_Invalid); +} + +StringRef XtensaMCExpr::getVariantKindName(VariantKind Kind) { + switch (Kind) { + default: + llvm_unreachable("Invalid ELF symbol kind"); + } +} diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCExpr.h b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCExpr.h new file mode 100644 index 0000000000000..f72b832024369 --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCExpr.h @@ -0,0 +1,58 @@ +//===-- XtensaMCExpr.h - Xtensa specific MC expression classes --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file describes Xtensa-specific MCExprs +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Xtensa_MCTARGETDESC_XtensaMCEXPR_H +#define LLVM_LIB_TARGET_Xtensa_MCTARGETDESC_XtensaMCEXPR_H + +#include "llvm/MC/MCExpr.h" + +namespace llvm { + +class StringRef; +class XtensaMCExpr : public MCTargetExpr { +public: + enum VariantKind { VK_Xtensa_None, VK_Xtensa_Invalid }; + +private: + const MCExpr *Expr; + const VariantKind Kind; + + explicit XtensaMCExpr(const MCExpr *Expr, VariantKind Kind) + : Expr(Expr), Kind(Kind) {} + +public: + static const XtensaMCExpr *create(const MCExpr *Expr, VariantKind Kind, + MCContext &Ctx); + + VariantKind getKind() const { return Kind; } + + const MCExpr *getSubExpr() const { return Expr; } + + void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; + bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, + const MCFixup *Fixup) const override; + void visitUsedExpr(MCStreamer &Streamer) const override; + MCFragment *findAssociatedFragment() const override { + return getSubExpr()->findAssociatedFragment(); + } + + void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override {} + + static VariantKind getVariantKindForName(StringRef name); + static StringRef getVariantKindName(VariantKind Kind); +}; + +} // end namespace llvm. + +#endif diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCTargetDesc.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCTargetDesc.cpp new file mode 100644 index 0000000000000..5e80d8c19aa00 --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCTargetDesc.cpp @@ -0,0 +1,121 @@ +//===-- XtensaMCTargetDesc.cpp - Xtebsa target descriptions ---------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "XtensaMCTargetDesc.h" +#include "XtensaInstPrinter.h" +#include "XtensaMCAsmInfo.h" +#include "llvm/MC/MCDwarf.h" +#include "XtensaTargetStreamer.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/ErrorHandling.h" + +#define GET_INSTRINFO_MC_DESC +#include "XtensaGenInstrInfo.inc" + +#define GET_REGINFO_MC_DESC +#include "XtensaGenRegisterInfo.inc" + +#define GET_SUBTARGETINFO_MC_DESC +#include "XtensaGenSubtargetInfo.inc" + +using namespace llvm; + +static MCAsmInfo *createXtensaMCAsmInfo(const MCRegisterInfo &MRI, + const Triple &TT, + const MCTargetOptions &Options) { + MCAsmInfo *MAI = new XtensaMCAsmInfo(TT); + MCCFIInstruction Inst = MCCFIInstruction::cfiDefCfa( + nullptr, MRI.getDwarfRegNum(Xtensa::SP, true), 0); + MAI->addInitialFrameState(Inst); + return MAI; +} + +static MCInstrInfo *createXtensaMCInstrInfo() { + MCInstrInfo *X = new MCInstrInfo(); + InitXtensaMCInstrInfo(X); + return X; +} + +static MCInstPrinter *createXtensaMCInstPrinter(const Triple &TT, + unsigned SyntaxVariant, + const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI) { + return new XtensaInstPrinter(MAI, MII, MRI); +} + +static MCRegisterInfo *createXtensaMCRegisterInfo(const Triple &TT) { + MCRegisterInfo *X = new MCRegisterInfo(); + InitXtensaMCRegisterInfo(X, Xtensa::SP); + return X; +} + +static MCSubtargetInfo * +createXtensaMCSubtargetInfo(const Triple &TT, StringRef CPU, StringRef FS) { + if (CPU.empty()) + CPU = "esp32"; + else if (CPU == "esp32-s2") + CPU = "esp32s2"; + else if (CPU == "esp32-s3") + CPU = "esp32s3"; + return createXtensaMCSubtargetInfoImpl(TT, CPU, CPU, FS); +} + +static MCTargetStreamer * +createXtensaAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS, + MCInstPrinter *InstPrint, bool isVerboseAsm) { + return new XtensaTargetAsmStreamer(S, OS); +} + +static MCTargetStreamer * +createXtensaObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { + return new XtensaTargetELFStreamer(S); +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeXtensaTargetMC() { + // Register the MCAsmInfo. + TargetRegistry::RegisterMCAsmInfo(TheXtensaTarget, createXtensaMCAsmInfo); + + // Register the MCCodeEmitter. + TargetRegistry::RegisterMCCodeEmitter(TheXtensaTarget, + createXtensaMCCodeEmitter); + + // Register the MCInstrInfo. + TargetRegistry::RegisterMCInstrInfo(TheXtensaTarget, createXtensaMCInstrInfo); + + // Register the MCInstPrinter. + TargetRegistry::RegisterMCInstPrinter(TheXtensaTarget, + createXtensaMCInstPrinter); + + // Register the MCRegisterInfo. + TargetRegistry::RegisterMCRegInfo(TheXtensaTarget, + createXtensaMCRegisterInfo); + + // Register the MCSubtargetInfo. + TargetRegistry::RegisterMCSubtargetInfo(TheXtensaTarget, + createXtensaMCSubtargetInfo); + + // Register the MCAsmBackend. + TargetRegistry::RegisterMCAsmBackend(TheXtensaTarget, + createXtensaMCAsmBackend); + + // Register the asm target streamer. + TargetRegistry::RegisterAsmTargetStreamer(TheXtensaTarget, + createXtensaAsmTargetStreamer); + + // Register the ELF target streamer. + TargetRegistry::RegisterObjectTargetStreamer( + TheXtensaTarget, createXtensaObjectTargetStreamer); +} diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCTargetDesc.h b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCTargetDesc.h new file mode 100644 index 0000000000000..daa3f5cce70f6 --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCTargetDesc.h @@ -0,0 +1,60 @@ +//===-- XtensaMCTargetDesc.h - Xtensa Target Descriptions -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides Xtensa specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_MCTARGETDESC_XTENSAMCTARGETDESC_H +#define LLVM_LIB_TARGET_XTENSA_MCTARGETDESC_XTENSAMCTARGETDESC_H +#include "llvm/Support/DataTypes.h" +#include + +namespace llvm { + +class MCAsmBackend; +class MCCodeEmitter; +class MCContext; +class MCInstrInfo; +class MCObjectTargetWriter; +class MCObjectWriter; +class MCRegisterInfo; +class MCSubtargetInfo; +class MCTargetOptions; +class StringRef; +class Target; +class raw_ostream; + +extern Target TheXtensaTarget; + +MCCodeEmitter *createXtensaMCCodeEmitter(const MCInstrInfo &MCII, + MCContext &Ctx); + +MCAsmBackend *createXtensaMCAsmBackend(const Target &T, + const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options); +std::unique_ptr +createXtensaObjectWriter(uint8_t OSABI, bool IsLittleEndian); +} // end namespace llvm + +// Defines symbolic names for Xtensa registers. +// This defines a mapping from register name to register number. +#define GET_REGINFO_ENUM +#include "XtensaGenRegisterInfo.inc" + +// Defines symbolic names for the Xtensa instructions. +#define GET_INSTRINFO_ENUM +#include "XtensaGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_ENUM +#include "XtensaGenSubtargetInfo.inc" + +#endif /* LLVM_LIB_TARGET_XTENSA_MCTARGETDESC_XTENSAMCTARGETDESC_H */ diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaTargetStreamer.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaTargetStreamer.cpp new file mode 100644 index 0000000000000..134193821c85b --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaTargetStreamer.cpp @@ -0,0 +1,109 @@ +//===-- XtensaTargetStreamer.cpp - Xtensa Target Streamer Methods ---------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides Xtensa specific target streamer methods. +// +//===----------------------------------------------------------------------===// + +#include "XtensaTargetStreamer.h" +#include "XtensaInstPrinter.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/Support/FormattedStream.h" + +using namespace llvm; + +XtensaTargetStreamer::XtensaTargetStreamer(MCStreamer &S) + : MCTargetStreamer(S) {} + +XtensaTargetAsmStreamer::XtensaTargetAsmStreamer(MCStreamer &S, + formatted_raw_ostream &OS) + : XtensaTargetStreamer(S), OS(OS) {} + +void XtensaTargetAsmStreamer::emitLiteral(std::string str) { OS << str; } + +XtensaTargetELFStreamer::XtensaTargetELFStreamer(MCStreamer &S) + : XtensaTargetStreamer(S) {} + +void XtensaTargetELFStreamer::emitLiteralLabel(MCSymbol *LblSym, SMLoc L) { + MCContext &Context = getStreamer().getContext(); + MCStreamer &OutStreamer = getStreamer(); + StringRef LiteralSectionPrefix = getLiteralSectionPrefix(); + std::string SectionName; + + if (LiteralSectionPrefix != "") { + SectionName = LiteralSectionPrefix.str() + ".literal"; + } else { + MCSectionELF *CS = (MCSectionELF *)OutStreamer.getCurrentSectionOnly(); + std::string CSectionName = CS->getName().str(); + std::size_t Pos = CSectionName.find(".text"); + if (Pos != std::string::npos) { + SectionName = ".literal"; + SectionName += CSectionName.substr(Pos); + } else { + SectionName = CSectionName; + SectionName += ".literal"; + } + } + + MCSection *ConstSection = Context.getELFSection( + SectionName, ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC); + ConstSection->setAlignment(Align(4)); + + OutStreamer.pushSection(); + OutStreamer.switchSection(ConstSection); + OutStreamer.emitLabel(LblSym, L); + OutStreamer.popSection(); +} + +void XtensaTargetELFStreamer::emitLiteral(MCSymbol *LblSym, const MCExpr *Value, + SMLoc L) { + MCStreamer &OutStreamer = getStreamer(); + + OutStreamer.emitLabel(LblSym, L); + OutStreamer.emitValue(Value, 4, L); +} + +void XtensaTargetELFStreamer::emitLiteral(const MCExpr *Value, SMLoc L) { + MCContext &Context = getStreamer().getContext(); + MCStreamer &OutStreamer = getStreamer(); + MCSectionELF *CS = (MCSectionELF *)OutStreamer.getCurrentSectionOnly(); + StringRef LiteralSectionPrefix = getLiteralSectionPrefix(); + std::string SectionName; + + if (LiteralSectionPrefix != "") { + SectionName = LiteralSectionPrefix.str() + ".literal"; + } else { + std::string CSectionName = CS->getName().str(); + std::size_t Pos = CSectionName.find(".text"); + if (Pos != std::string::npos) { + SectionName = ".literal"; + SectionName += CSectionName.substr(Pos); + } else { + SectionName = CSectionName; + SectionName += ".literal"; + } + } + + MCSection *ConstSection = Context.getELFSection( + SectionName, ELF::SHT_PROGBITS, ELF::SHF_EXECINSTR | ELF::SHF_ALLOC); + + OutStreamer.pushSection(); + OutStreamer.switchSection(ConstSection); + OutStreamer.emitValue(Value, 4, L); + OutStreamer.popSection(); +} + +MCELFStreamer &XtensaTargetELFStreamer::getStreamer() { + return static_cast(Streamer); +} diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaTargetStreamer.h b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaTargetStreamer.h new file mode 100644 index 0000000000000..ef03578862a26 --- /dev/null +++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaTargetStreamer.h @@ -0,0 +1,57 @@ +//===-- XtensaTargetStreamer.h - Xtensa Target Streamer --------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSATARGETSTREAMER_H +#define LLVM_LIB_TARGET_XTENSA_XTENSATARGETSTREAMER_H + +#include "XtensaConstantPoolValue.h" +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/SMLoc.h" + +namespace llvm { +class formatted_raw_ostream; + +class XtensaTargetStreamer : public MCTargetStreamer { + StringRef LiteralSectionPrefix = ""; + +public: + XtensaTargetStreamer(MCStreamer &S); + virtual void emitLiteral(MCSymbol *LblSym, const MCExpr *Value, SMLoc L) = 0; + virtual void emitLiteralLabel(MCSymbol *LblSym, SMLoc L) = 0; + virtual void emitLiteral(const MCExpr *Value, SMLoc L) = 0; + virtual void emitLiteral(std::string str) = 0; + void setLiteralSectionPrefix(StringRef Name) { LiteralSectionPrefix = Name; } + StringRef getLiteralSectionPrefix() { return LiteralSectionPrefix; } +}; + +class XtensaTargetAsmStreamer : public XtensaTargetStreamer { + formatted_raw_ostream &OS; + +public: + XtensaTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS); + void emitLiteral(MCSymbol *LblSym, const MCExpr *Value, SMLoc L) override {} + void emitLiteralLabel(MCSymbol *LblSym, SMLoc L) override {} + void emitLiteral(const MCExpr *Value, SMLoc L) override {} + void emitLiteral(std::string str) override; +}; + +class XtensaTargetELFStreamer : public XtensaTargetStreamer { +public: + XtensaTargetELFStreamer(MCStreamer &S); + MCELFStreamer &getStreamer(); + void emitLiteral(MCSymbol *LblSym, const MCExpr *Value, SMLoc L) override; + void emitLiteralLabel(MCSymbol *LblSym, SMLoc L) override; + void emitLiteral(const MCExpr *Value, SMLoc L) override; + void emitLiteral(std::string str) override {} +}; +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/Xtensa/TargetInfo/CMakeLists.txt b/llvm/lib/Target/Xtensa/TargetInfo/CMakeLists.txt new file mode 100644 index 0000000000000..870f875ccfdd0 --- /dev/null +++ b/llvm/lib/Target/Xtensa/TargetInfo/CMakeLists.txt @@ -0,0 +1,11 @@ +include_directories( ${CMAKE_CURRENT_BINARY_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/.. ) + +add_llvm_component_library(LLVMXtensaInfo + XtensaTargetInfo.cpp + + LINK_COMPONENTS + Support + + ADD_TO_COMPONENT + Xtensa + ) diff --git a/llvm/lib/Target/Xtensa/TargetInfo/XtensaTargetInfo.cpp b/llvm/lib/Target/Xtensa/TargetInfo/XtensaTargetInfo.cpp new file mode 100644 index 0000000000000..7fe1bc793119b --- /dev/null +++ b/llvm/lib/Target/Xtensa/TargetInfo/XtensaTargetInfo.cpp @@ -0,0 +1,20 @@ +//===-- XtensaTargetInfo.cpp - Xtensa Target Implementation ---------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/TargetRegistry.h" + +using namespace llvm; +namespace llvm { +Target TheXtensaTarget; +} +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeXtensaTargetInfo() { + RegisterTarget X(TheXtensaTarget, "xtensa", "Xtensa 32", + "XTENSA"); +} diff --git a/llvm/lib/Target/Xtensa/Xtensa.h b/llvm/lib/Target/Xtensa/Xtensa.h new file mode 100644 index 0000000000000..5f7c7292a51bc --- /dev/null +++ b/llvm/lib/Target/Xtensa/Xtensa.h @@ -0,0 +1,35 @@ +//===- Xtensa.h - Top-level interface for Xtensa representation -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the entry points for global functions defined in +// the LLVM Xtensa back-end. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSA_H +#define LLVM_LIB_TARGET_XTENSA_XTENSA_H + +#include "MCTargetDesc/XtensaMCTargetDesc.h" +#include "llvm/PassRegistry.h" +#include "llvm/Support/CodeGen.h" + +namespace llvm { +class XtensaTargetMachine; +class FunctionPass; + +FunctionPass *createXtensaISelDag(XtensaTargetMachine &TM, + CodeGenOpt::Level OptLevel); +FunctionPass *createXtensaSizeReductionPass(); +FunctionPass *createXtensaHardwareLoops(); +FunctionPass *createXtensaFixupHwLoops(); +FunctionPass *createXtensaPSRAMCacheFixPass(); +FunctionPass *createXtensaConstantIslandPass(); +} // namespace llvm +#endif /* LLVM_LIB_TARGET_XTENSA_XTENSA_H */ diff --git a/llvm/lib/Target/Xtensa/Xtensa.td b/llvm/lib/Target/Xtensa/Xtensa.td new file mode 100644 index 0000000000000..d230c631257fa --- /dev/null +++ b/llvm/lib/Target/Xtensa/Xtensa.td @@ -0,0 +1,235 @@ +//===- Xtensa.td - Describe the Xtensa Target Machine ------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Target-independent interfaces +//===----------------------------------------------------------------------===// + +include "llvm/Target/Target.td" + +//===----------------------------------------------------------------------===// +// Subtarget Features. +//===----------------------------------------------------------------------===// +def FeatureDensity : SubtargetFeature<"density", "HasDensity", "true", + "Enable Density instructions">; +def HasDensity : Predicate<"Subtarget->hasDensity()">, + AssemblerPredicate<(all_of FeatureDensity)>; + +def FeatureSingleFloat : SubtargetFeature<"fp", "HasSingleFloat", "true", + "Enable Xtensa Single FP instructions">; +def HasSingleFloat : Predicate<"Subtarget->hasSingleFloat()">, + AssemblerPredicate<(all_of FeatureSingleFloat)>; + +def FeatureWindowed : SubtargetFeature<"windowed", "HasWindowed", "true", + "Enable Xtensa Windowed Register option">; +def HasWindowed : Predicate<"Subtarget->hasWindowed()">, + AssemblerPredicate<(all_of FeatureWindowed)>; + +def FeatureBoolean : SubtargetFeature<"bool", "HasBoolean", "true", + "Enable Xtensa Boolean extension">; +def HasBoolean : Predicate<"Subtarget->hasBoolean()">, + AssemblerPredicate<(all_of FeatureBoolean)>; + +def FeatureLoop : SubtargetFeature<"loop", "HasLoop", "true", + "Enable Xtensa Loop extension">; +def HasLoop : Predicate<"Subtarget->hasLoop()">, + AssemblerPredicate<(all_of FeatureLoop)>; + +def FeatureSEXT : SubtargetFeature<"sext", "HasSEXT", "true", + "Enable Xtensa Sign Extend option">; +def HasSEXT : Predicate<"Subtarget->hasSEXT()">, + AssemblerPredicate<(all_of FeatureSEXT)>; + +def FeatureNSA : SubtargetFeature<"nsa", "HasNSA", "true", + "Enable Xtensa NSA option">; +def HasNSA : Predicate<"Subtarget->hasNSA()">, + AssemblerPredicate<(all_of FeatureNSA)>; + +def FeatureMul16 : SubtargetFeature<"mul16", "HasMul16", "true", + "Enable Xtensa Mul16 option">; +def HasMul16 : Predicate<"Subtarget->hasMul16()">, + AssemblerPredicate<(all_of FeatureMul16)>; + +def FeatureMul32 : SubtargetFeature<"mul32", "HasMul32", "true", + "Enable Xtensa Mul32 option">; +def HasMul32 : Predicate<"Subtarget->hasMul32()">, + AssemblerPredicate<(all_of FeatureMul32)>; + +def FeatureMul32High : SubtargetFeature<"mul32high", "HasMul32High", "true", + "Enable Xtensa Mul32High option">; +def HasMul32High : Predicate<"Subtarget->hasMul32High()">, + AssemblerPredicate<(all_of FeatureMul32High)>; + +def FeatureDiv32 : SubtargetFeature<"div32", "HasDiv32", "true", + "Enable Xtensa Div32 option">; +def HasDiv32 : Predicate<"Subtarget->hasDiv32()">, + AssemblerPredicate<(all_of FeatureDiv32)>; + +def FeatureMAC16 : SubtargetFeature<"mac16", "HasMAC16", "true", + "Enable Xtensa MAC16 instructions">; +def HasMAC16 : Predicate<"Subtarget->hasMAC16()">, + AssemblerPredicate<(all_of FeatureMAC16)>; + +def FeatureDFPAccel : SubtargetFeature<"dfpaccel", "HasDFPAccel", "true", + "Enable Xtensa Double Precision FP acceleration">; +def HasDFPAccel : Predicate<"Subtarget->hasDFPAccel()">, + AssemblerPredicate<(all_of FeatureDFPAccel)>; + +def FeatureS32C1I : SubtargetFeature<"s32c1i", "HasS32C1I", "true", + "Enable Xtensa S32C1I option">; +def HasS32C1I : Predicate<"Subtarget->hasS32C1I()">, + AssemblerPredicate<(all_of FeatureS32C1I)>; + +def FeatureTHREADPTR : SubtargetFeature<"threadptr", "HasTHREADPTR", "true", + "Enable Xtensa THREADPTR option">; +def HasTHREADPTR : Predicate<"Subtarget->hasTHREADPTR()">, + AssemblerPredicate<(all_of FeatureTHREADPTR)>; + +def FeatureExtendedL32R : SubtargetFeature<"extendedl32r", "HasExtendedL32R", "true", + "Enable Xtensa Extended L32R option">; +def HasExtendedL32R : Predicate<"Subtarget->hasExtendedL32R()">, + AssemblerPredicate<(all_of FeatureExtendedL32R)>; + +def FeatureATOMCTL : SubtargetFeature<"atomctl", "HasATOMCTL", "true", + "Enable Xtensa ATOMCTL option">; +def HasATOMCTL : Predicate<"Subtarget->hasATOMCTL()">, + AssemblerPredicate<(all_of FeatureATOMCTL)>; + +def FeatureMEMCTL : SubtargetFeature<"memctl", "HasMEMCTL", "true", + "Enable Xtensa MEMCTL option">; +def HasMEMCTL : Predicate<"Subtarget->hasMEMCTL()">, + AssemblerPredicate<(all_of FeatureMEMCTL)>; + +def FeatureDebug : SubtargetFeature<"debug", "HasDebug", "true", + "Enable Xtensa Debug option">; +def HasDebug : Predicate<"Subtarget->hasDebug()">, + AssemblerPredicate<(all_of FeatureDebug)>; + +def FeatureException : SubtargetFeature<"exception", "HasException", "true", + "Enable Xtensa Exception option">; +def HasException : Predicate<"Subtarget->hasException()">, + AssemblerPredicate<(all_of FeatureException)>; + +def FeatureHighPriInterrupts : SubtargetFeature<"highpriinterrupts", + "HasHighPriInterrupts", "true", + "Enable Xtensa HighPriInterrupts option">; +def HasHighPriInterrupts : Predicate<"Subtarget->hasHighPriInterrupts()">, + AssemblerPredicate<(all_of FeatureHighPriInterrupts)>; + +def FeatureCoprocessor : SubtargetFeature<"coprocessor", "HasCoprocessor", "true", + "Enable Xtensa Coprocessor option">; +def HasCoprocessor : Predicate<"Subtarget->hasCoprocessor()">, + AssemblerPredicate<(all_of FeatureCoprocessor)>; + +def FeatureInterrupt : SubtargetFeature<"interrupt", "HasInterrupt", "true", + "Enable Xtensa Interrupt option">; +def HasInterrupt : Predicate<"Subtarget->hasInterrupt()">, + AssemblerPredicate<(all_of FeatureInterrupt)>; + +def FeatureRelocatableVector : SubtargetFeature<"rvector", "HasRelocatableVector", "true", + "Enable Xtensa Relocatable Vector option">; +def HasRelocatableVector : Predicate<"Subtarget->hasRelocatableVector()">, + AssemblerPredicate<(all_of FeatureRelocatableVector)>; + +def FeatureTimerInt : SubtargetFeature<"timerint", "HasTimerInt", "true", + "Enable Xtensa Timer Interrupt option">; +def HasTimerInt : Predicate<"Subtarget->hasTimerInt()">, + AssemblerPredicate<(all_of FeatureTimerInt)>; + +def FeaturePRID : SubtargetFeature<"prid", "HasPRID", "true", + "Enable Xtensa Processor ID option">; +def HasPRID : Predicate<"Subtarget->hasPRID()">, + AssemblerPredicate<(all_of FeaturePRID)>; + +def FeatureRegionProtection : SubtargetFeature<"regprotect", "HasRegionProtection", "true", + "Enable Xtensa Region Protection option">; +def HasRegionProtection : Predicate<"Subtarget->hasRegionProtection()">, + AssemblerPredicate<(all_of FeatureRegionProtection)>; + +def FeatureMiscSR : SubtargetFeature<"miscsr", "HasMiscSR", "true", + "Enable Xtensa Miscellaneous SR option">; +def HasMiscSR : Predicate<"Subtarget->hasMiscSR()">, + AssemblerPredicate<(all_of FeatureMiscSR)>; + +def FeatureESP32S2Ops : SubtargetFeature<"esp32s2", "HasESP32S2Ops", "true", + "Support Xtensa esp32-s2 ISA extension">; +def HasESP32S2Ops : Predicate<"Subtarget->hasESP32S2Ops()">, + AssemblerPredicate<(all_of FeatureESP32S2Ops)>; + +def FeatureESP32S3Ops : SubtargetFeature<"esp32s3", "HasESP32S3Ops", "true", + "Support Xtensa esp32-s3 ISA extension">; +def HasESP32S3Ops : Predicate<"Subtarget->hasESP32S3Ops()">, + AssemblerPredicate<(all_of FeatureESP32S3Ops)>; + +//===----------------------------------------------------------------------===// +// Xtensa supported processors. +//===----------------------------------------------------------------------===// +class Proc Features> + : Processor; + +def : Proc<"generic", []>; + +def : Proc<"esp32", [FeatureDensity, FeatureSingleFloat, FeatureLoop, FeatureMAC16, FeatureWindowed, FeatureBoolean, FeatureSEXT, + FeatureNSA, FeatureMul16, FeatureMul32, FeatureMul32High, FeatureDFPAccel, FeatureS32C1I, FeatureTHREADPTR, FeatureDiv32, + FeatureATOMCTL, FeatureMEMCTL, FeatureDebug, FeatureException, FeatureHighPriInterrupts, FeatureCoprocessor, + FeatureInterrupt, FeatureRelocatableVector, FeatureTimerInt, FeaturePRID, FeatureRegionProtection, FeatureMiscSR]>; + +def : Proc<"esp8266", [FeatureDensity, FeatureNSA, FeatureMul16, FeatureMul32, FeatureExtendedL32R, FeatureDebug, FeatureException, + FeatureHighPriInterrupts, FeatureInterrupt, FeatureRelocatableVector, FeatureTimerInt, FeatureRegionProtection, FeaturePRID]>; + +def : Proc<"esp32s2", [FeatureDensity, FeatureWindowed, FeatureSEXT, FeatureNSA, FeatureMul16, FeatureMul32, FeatureMul32High, FeatureTHREADPTR, + FeatureDiv32, FeatureMEMCTL, FeatureDebug, FeatureException, FeatureHighPriInterrupts, FeatureCoprocessor, FeatureInterrupt, + FeatureRelocatableVector, FeatureTimerInt, FeaturePRID, FeatureRegionProtection, FeatureMiscSR, FeatureESP32S2Ops]>; + +def : Proc<"esp32s3", [FeatureDensity, FeatureSingleFloat, FeatureLoop, FeatureMAC16, FeatureWindowed, FeatureBoolean, FeatureSEXT, + FeatureNSA, FeatureMul16, FeatureMul32, FeatureMul32High, FeatureDFPAccel, FeatureS32C1I, FeatureTHREADPTR, FeatureDiv32, + FeatureATOMCTL, FeatureMEMCTL, FeatureDebug, FeatureException, FeatureHighPriInterrupts, FeatureCoprocessor, + FeatureInterrupt, FeatureRelocatableVector, FeatureTimerInt, FeaturePRID, FeatureRegionProtection, FeatureMiscSR, + FeatureESP32S3Ops]>; + +//===----------------------------------------------------------------------===// +// Register File Description +//===----------------------------------------------------------------------===// + +include "XtensaRegisterInfo.td" + +//===----------------------------------------------------------------------===// +// Calling Convention Description +//===----------------------------------------------------------------------===// + +include "XtensaCallingConv.td" + +//===----------------------------------------------------------------------===// +// Instruction Descriptions +//===----------------------------------------------------------------------===// + +include "XtensaInstrInfo.td" + +def XtensaInstrInfo : InstrInfo; + +//===----------------------------------------------------------------------===// +// Target Declaration +//===----------------------------------------------------------------------===// + +def XtensaAsmParser : AsmParser { + let ShouldEmitMatchRegisterAltName = 1; +} + +def XtensaInstPrinter : AsmWriter { + string AsmWriterClassName = "InstPrinter"; + bit isMCAsmWriter = 1; +} + +def Xtensa : Target { + let InstructionSet = XtensaInstrInfo; + let AssemblyWriters = [XtensaInstPrinter]; + let AssemblyParsers = [XtensaAsmParser]; +} + diff --git a/llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp b/llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp new file mode 100644 index 0000000000000..75a830208453f --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp @@ -0,0 +1,332 @@ +//===- XtensaAsmPrinter.cpp Xtensa LLVM Assembly Printer ------------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains a printer that converts from our internal representation +// of machine-dependent LLVM code to GAS-format Xtensa assembly language. +// +//===----------------------------------------------------------------------===// + +#include "XtensaAsmPrinter.h" +#include "MCTargetDesc/XtensaInstPrinter.h" +#include "XtensaConstantPoolValue.h" +#include "XtensaMCInstLower.h" +#include "XtensaSubtarget.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/CodeGen/MachineModuleInfoImpls.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInstBuilder.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/MC/TargetRegistry.h" + +using namespace llvm; + +static MCSymbolRefExpr::VariantKind +getModifierVariantKind(XtensaCP::XtensaCPModifier Modifier) { + switch (Modifier) { + case XtensaCP::no_modifier: + return MCSymbolRefExpr::VK_None; + case XtensaCP::TPOFF: + return MCSymbolRefExpr::VK_TPOFF; + } + llvm_unreachable("Invalid XtensaCPModifier!"); +} + +void XtensaAsmPrinter::emitInstruction(const MachineInstr *MI) { + XtensaMCInstLower Lower(MF->getContext(), *this); + MCInst LoweredMI; + unsigned Opc = MI->getOpcode(); + const MachineConstantPool *MCP = MF->getConstantPool(); + + // If we just ended a constant pool, mark it as such. + if (InConstantPool && Opc != Xtensa::CONSTPOOL_ENTRY) { + OutStreamer->emitDataRegion(MCDR_DataRegionEnd); + InConstantPool = false; + } + + if (Opc == Xtensa::CONSTPOOL_ENTRY) { + // CONSTPOOL_ENTRY - This instruction represents a floating + // constant pool in the function. The first operand is the ID# + // for this instruction, the second is the index into the + // MachineConstantPool that this is, the third is the size in + // bytes of this constant pool entry. + // The required alignment is specified on the basic block holding this MI. + // + unsigned LabelId = (unsigned)MI->getOperand(0).getImm(); + unsigned CPIdx = (unsigned)MI->getOperand(1).getIndex(); + + // If this is the first entry of the pool, mark it. + if (!InConstantPool) { + if (OutStreamer->hasRawTextSupport()) { + OutStreamer->emitRawText(StringRef("\t.literal_position\n")); + } + OutStreamer->emitDataRegion(MCDR_DataRegion); + InConstantPool = true; + } + const MachineConstantPoolEntry &MCPE = MCP->getConstants()[CPIdx]; + + emitMachineConstantPoolEntry(MCPE, LabelId); + return; + } + + switch (Opc) { + case Xtensa::BR_JT: { + EmitToStreamer( + *OutStreamer, + MCInstBuilder(Xtensa::JX).addReg(MI->getOperand(0).getReg())); + return; + } + case Xtensa::LOOPEND: + return; + } + Lower.lower(MI, LoweredMI); + EmitToStreamer(*OutStreamer, LoweredMI); +} + +/// Emit single constant pool entry to asm or obj file. +/// Use i argument to define entry label. +void XtensaAsmPrinter::emitMachineConstantPoolEntry( + const MachineConstantPoolEntry &CPE, int i) { + if (CPE.isMachineConstantPoolEntry()) { + XtensaConstantPoolValue *ACPV = + static_cast(CPE.Val.MachineCPVal); + ACPV->setLabelId(i); + emitMachineConstantPoolValue(CPE.Val.MachineCPVal); + } else { + MCSymbol *LblSym = GetCPISymbol(i); + // TODO find a better way to check whether we emit data to .s file + if (OutStreamer->hasRawTextSupport()) { + std::string str("\t.literal "); + str += LblSym->getName(); + str += ", "; + const Constant *C = CPE.Val.ConstVal; + + Type *Ty = C->getType(); + if (const auto *CFP = dyn_cast(C)) { + str += toString(CFP->getValueAPF().bitcastToAPInt(), 10, true); + } else if (const auto *CI = dyn_cast(C)) { + str += toString(CI->getValue(), 10, true); + } else if (isa(Ty)) { + const MCExpr *ME = lowerConstant(C); + const MCSymbolRefExpr &SRE = cast(*ME); + const MCSymbol &Sym = SRE.getSymbol(); + str += Sym.getName(); + } else { + unsigned NumElements; + if (isa(Ty)) + NumElements = (cast(Ty))->getNumElements(); + else + NumElements = Ty->getArrayNumElements(); + + for (unsigned I = 0; I < NumElements; I++) { + const Constant *CAE = C->getAggregateElement(I); + if (I > 0) + str += ", "; + if (const auto *CFP = dyn_cast(CAE)) { + str += toString(CFP->getValueAPF().bitcastToAPInt(), 10, true); + } else if (const auto *CI = dyn_cast(CAE)) { + str += toString(CI->getValue(), 10, true); + } + } + } + + OutStreamer->emitRawText(StringRef(str)); + } else { + OutStreamer->emitCodeAlignment( + 4, OutStreamer->getContext().getSubtargetInfo()); + OutStreamer->emitLabel(LblSym); + emitGlobalConstant(getDataLayout(), CPE.Val.ConstVal); + } + } +} + +/// EmitConstantPool - Print to the current output stream assembly +/// representations of the constants in the constant pool MCP. This is +/// used to print out constants which have been "spilled to memory" by +/// the code generator. +void XtensaAsmPrinter::emitConstantPool() { + const Function &F = MF->getFunction(); + const MachineConstantPool *MCP = MF->getConstantPool(); + const std::vector &CP = MCP->getConstants(); + const XtensaSubtarget *Subtarget = &MF->getSubtarget(); + + if (Subtarget->useTextSectionLiterals()) + return; + + if (CP.empty()) + return; + + for (unsigned i = 0, e = CP.size(); i != e; ++i) { + const MachineConstantPoolEntry &CPE = CP[i]; + + if (i == 0) { + if (OutStreamer->hasRawTextSupport()) { + OutStreamer->switchSection( + getObjFileLowering().SectionForGlobal(&F, TM)); + OutStreamer->emitRawText(StringRef("\t.literal_position\n")); + } else { + MCSectionELF *CS = + (MCSectionELF *)getObjFileLowering().SectionForGlobal(&F, TM); + std::string CSectionName = CS->getName().str(); + std::string SectionName; + std::size_t Pos = CSectionName.find(".text"); + if (Pos != std::string::npos) { + if (Pos > 0) + SectionName = CSectionName.substr(0, Pos + 5); + else + SectionName = ""; + SectionName += ".literal"; + SectionName += CSectionName.substr(Pos + 5); + } else { + SectionName = CSectionName; + SectionName += ".literal"; + } + + MCSectionELF *S = + OutContext.getELFSection(SectionName, ELF::SHT_PROGBITS, + ELF::SHF_EXECINSTR | ELF::SHF_ALLOC); + S->setAlignment(Align(4)); + OutStreamer->switchSection(S); + } + } + + emitMachineConstantPoolEntry(CPE, i); + } +} + +void XtensaAsmPrinter::emitMachineConstantPoolValue( + MachineConstantPoolValue *MCPV) { + XtensaConstantPoolValue *ACPV = static_cast(MCPV); + + MCSymbol *MCSym; + if (ACPV->isBlockAddress()) { + const BlockAddress *BA = + cast(ACPV)->getBlockAddress(); + MCSym = GetBlockAddressSymbol(BA); + } else if (ACPV->isGlobalValue()) { + const GlobalValue *GV = cast(ACPV)->getGV(); + // TODO some modifiers + MCSym = getSymbol(GV); + } else if (ACPV->isMachineBasicBlock()) { + const MachineBasicBlock *MBB = cast(ACPV)->getMBB(); + MCSym = MBB->getSymbol(); + } else if (ACPV->isJumpTable()) { + unsigned idx = cast(ACPV)->getIndex(); + MCSym = this->GetJTISymbol(idx, false); + } else { + assert(ACPV->isExtSymbol() && "unrecognized constant pool value"); + XtensaConstantPoolSymbol *XtensaSym = cast(ACPV); + const char *Sym = XtensaSym->getSymbol(); + // TODO it's a trick to distinguish static references and generated rodata + // references Some clear method required + { + std::string SymName(Sym); + if (XtensaSym->isPrivateLinkage()) + SymName = ".L" + SymName; + MCSym = GetExternalSymbolSymbol(StringRef(SymName)); + } + } + + MCSymbol *LblSym = GetCPISymbol(ACPV->getLabelId()); + // TODO find a better way to check whether we emit data to .s file + if (OutStreamer->hasRawTextSupport()) { + std::string SymName("\t.literal "); + SymName += LblSym->getName(); + SymName += ", "; + SymName += MCSym->getName(); + + StringRef Modifier = ACPV->getModifierText(); + SymName += Modifier; + + OutStreamer->emitRawText(StringRef(SymName)); + } else { + MCSymbolRefExpr::VariantKind VK = + getModifierVariantKind(ACPV->getModifier()); + + if (ACPV->getModifier() != XtensaCP::no_modifier) { + std::string SymName(MCSym->getName()); + MCSym = GetExternalSymbolSymbol(StringRef(SymName)); + } + + const MCExpr *Expr = MCSymbolRefExpr::create(MCSym, VK, OutContext); + uint64_t Size = getDataLayout().getTypeAllocSize(ACPV->getType()); + OutStreamer->emitCodeAlignment( + 4, OutStreamer->getContext().getSubtargetInfo()); + OutStreamer->emitLabel(LblSym); + OutStreamer->emitValue(Expr, Size); + } +} + +void XtensaAsmPrinter::printOperand(const MachineInstr *MI, int OpNo, + raw_ostream &O) { + const MachineOperand &MO = MI->getOperand(OpNo); + + switch (MO.getType()) { + case MachineOperand::MO_Register: + case MachineOperand::MO_Immediate: { + XtensaMCInstLower Lower(MF->getContext(), *this); + MCOperand MC(Lower.lowerOperand(MI->getOperand(OpNo))); + XtensaInstPrinter::printOperand(MC, O); + break; + } + case MachineOperand::MO_GlobalAddress: + O << *getSymbol(MO.getGlobal()); + break; + default: + llvm_unreachable(""); + } + + if (MO.getTargetFlags()) { + O << ")"; + } +} + +bool XtensaAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + const char *ExtraCode, raw_ostream &O) { + if (ExtraCode && *ExtraCode == 'n') { + if (!MI->getOperand(OpNo).isImm()) + return true; + O << -int64_t(MI->getOperand(OpNo).getImm()); + } else { + printOperand(MI, OpNo, O); + } + return false; +} + +bool XtensaAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, + unsigned OpNo, + const char *ExtraCode, + raw_ostream &OS) { + if (ExtraCode && ExtraCode[0]) + return true; // Unknown modifier. + + assert(OpNo + 1 < MI->getNumOperands() && "Insufficient operands"); + + const MachineOperand &Base = MI->getOperand(OpNo); + const MachineOperand &Offset = MI->getOperand(OpNo + 1); + + assert(Base.isReg() && + "Unexpected base pointer for inline asm memory operand."); + assert(Offset.isImm() && "Unexpected offset for inline asm memory operand."); + + OS << XtensaInstPrinter::getRegisterName(Base.getReg()); + OS << ", "; + OS << Offset.getImm(); + + return false; +} + +// Force static initialization. +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeXtensaAsmPrinter() { + RegisterAsmPrinter A(TheXtensaTarget); +} diff --git a/llvm/lib/Target/Xtensa/XtensaAsmPrinter.h b/llvm/lib/Target/Xtensa/XtensaAsmPrinter.h new file mode 100644 index 0000000000000..dbf092f671237 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaAsmPrinter.h @@ -0,0 +1,54 @@ +//===- XtensaAsmPrinter.h - Xtensa LLVM Assembly Printer --------*- C++-*--===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Xtensa Assembly printer class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSAASMPRINTER_H +#define LLVM_LIB_TARGET_XTENSA_XTENSAASMPRINTER_H + +#include "XtensaTargetMachine.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/Support/Compiler.h" + +namespace llvm { +class MCStreamer; +class MachineBasicBlock; +class MachineInstr; +class Module; +class raw_ostream; + +class LLVM_LIBRARY_VISIBILITY XtensaAsmPrinter : public AsmPrinter { + const MCSubtargetInfo *STI; + /// InConstantPool - Maintain state when emitting a sequence of constant + /// pool entries so we can properly mark them as data regions. + bool InConstantPool = false; +public: + explicit XtensaAsmPrinter(TargetMachine &TM, + std::unique_ptr Streamer) + : AsmPrinter(TM, std::move(Streamer)), STI(TM.getMCSubtargetInfo()) {} + + // Override AsmPrinter. + StringRef getPassName() const override { return "Xtensa Assembly Printer"; } + void emitInstruction(const MachineInstr *MI) override; + void emitConstantPool() override; + void emitMachineConstantPoolEntry(const MachineConstantPoolEntry &CPE, int i); + void emitMachineConstantPoolValue(MachineConstantPoolValue *MCPV) override; + void printOperand(const MachineInstr *MI, int opNum, raw_ostream &O); + bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + const char *ExtraCode, raw_ostream &O) override; + bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, + const char *ExtraCode, raw_ostream &OS) override; +}; +} // end namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_XTENSAASMPRINTER_H */ diff --git a/llvm/lib/Target/Xtensa/XtensaCallingConv.td b/llvm/lib/Target/Xtensa/XtensaCallingConv.td new file mode 100644 index 0000000000000..adfb8656b32d3 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaCallingConv.td @@ -0,0 +1,48 @@ +//===- XtensaCallingConv.td - Xtensa Calling Conventions -*- tablegen ---*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This describes the calling conventions for the Xtensa ABI. +//===----------------------------------------------------------------------===// + +/// CCIfAlign - Match of the original alignment of the arg +class CCIfAlign + : CCIf; + +//===----------------------------------------------------------------------===// +// Xtensa return value calling convention +//===----------------------------------------------------------------------===// +def RetCC_Xtensa : CallingConv<[ + CCIfType<[i1, i8, i16], CCPromoteToType>, + CCIfType<[f32], CCBitConvertToType>, + + //First two return values go in a2, a3, a4, a5 + CCIfType<[i32], CCAssignToReg<[A2, A3, A4, A5]>>, + CCIfType<[f32], CCAssignToReg<[A2, A3, A4, A5]>>, + CCIfType<[i64], CCAssignToRegWithShadow<[A2, A4], [A3, A5]>> +]>; + +//===----------------------------------------------------------------------===// +// Callee-saved register lists. +//===----------------------------------------------------------------------===// + +def CSR_Xtensa : CalleeSavedRegs<(add A0, A12, A13, A14, A15)>; +def CSRWE_Xtensa : CalleeSavedRegs<(add)> { + let OtherPreserved = (add A0, SP, A2, A3, A4, A5, A6, A7); +} +//===----------------------------------------------------------------------===// + +def RetCCW_Xtensa : CallingConv<[ + CCIfType<[i1, i8, i16], CCPromoteToType>, + CCIfType<[f32], CCBitConvertToType>, + + //First two return values go in a10, a11, a12, a13 + CCIfType<[i32], CCAssignToReg<[A10, A11, A12, A13]>>, + CCIfType<[f32], CCAssignToReg<[A10, A11, A12, A13]>>, + CCIfType<[i64], CCAssignToRegWithShadow<[A10, A12], [A11, A13]>> +]>; diff --git a/llvm/lib/Target/Xtensa/XtensaConstantIsland.cpp b/llvm/lib/Target/Xtensa/XtensaConstantIsland.cpp new file mode 100644 index 0000000000000..cc010a02522ab --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaConstantIsland.cpp @@ -0,0 +1,1471 @@ +//===- XtensaConstantIslandPass.cpp - Emit Pc Relative loads +//----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass is used to make Pc relative loads of constants. +// +// Loading constants inline is expensive on Xtensa and it's in general better +// to place the constant nearby in code space and then it can be loaded with a +// simple l32r instruction. +// +// The constants can be not just numbers but addresses of functions and labels. +// This can be particularly helpful in static relocation mode for embedded +// non-linux targets. +// +//===----------------------------------------------------------------------===// + +#include "Xtensa.h" +#include "XtensaConstantPoolValue.h" +#include "XtensaMachineFunctionInfo.h" +#include "XtensaSubtarget.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/CodeGen/LivePhysRegs.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "xtensa-constant-islands" + +STATISTIC(NumCPEs, "Number of constpool entries"); +STATISTIC(NumSplit, "Number of uncond branches inserted"); +STATISTIC(NumCBrFixed, "Number of cond branches fixed"); +STATISTIC(NumUBrFixed, "Number of uncond branches fixed"); + +// FIXME: This option should be removed once it has received sufficient testing. +static cl::opt + AlignConstantIslands("xtensa-align-constant-islands", cl::Hidden, + cl::init(true), + cl::desc("Align constant islands in code")); + +// Rather than do make check tests with huge amounts of code, we force +// the test to use this amount. +static cl::opt ConstantIslandsSmallOffset( + "xtensa-constant-islands-small-offset", cl::init(0), + cl::desc("Make small offsets be this amount for testing purposes"), + cl::Hidden); + +#define MAX_CP_ITERATIONS 30 +#define MAX_BR_ITERATIONS 30 + +// TODO +// This defines for L32R and J instruction displacemnt for +// testing purposes only +#define MAX_DISP_L32R 262144 +#define BITS_JUMP 18 + +static unsigned int branchTargetOperand(MachineInstr *MI) { + switch (MI->getOpcode()) { + case Xtensa::J: + return 0; + case Xtensa::BEQ: + case Xtensa::BNE: + case Xtensa::BLT: + case Xtensa::BLTU: + case Xtensa::BGE: + case Xtensa::BGEU: + case Xtensa::BEQI: + case Xtensa::BNEI: + case Xtensa::BLTI: + case Xtensa::BLTUI: + case Xtensa::BGEI: + case Xtensa::BGEUI: + return 2; + case Xtensa::BEQZ: + case Xtensa::BNEZ: + case Xtensa::BLTZ: + case Xtensa::BGEZ: + return 1; + case Xtensa::BT: + case Xtensa::BF: + return 1; + } + llvm_unreachable("Unknown branch type"); +} + +namespace { + +using Iter = MachineBasicBlock::iterator; +using ReverseIter = MachineBasicBlock::reverse_iterator; + +/// XtensaConstantIslands - Due to limited PC-relative displacements, Xtensa +/// requires constant pool entries to be scattered among the instructions +/// inside a function. To do this, it completely ignores the normal LLVM +/// constant pool; instead, it places constants wherever it feels like with +/// special instructions. +/// +/// The terminology used in this pass includes: +/// Islands - Clumps of constants placed in the function. +/// Water - Potential places where an island could be formed. +/// CPE - A constant pool entry that has been placed somewhere, which +/// tracks a list of users. + +class XtensaConstantIslands : public MachineFunctionPass { + /// BasicBlockInfo - Information about the offset and size of a single + /// basic block. + struct BasicBlockInfo { + /// Offset - Distance from the beginning of the function to the beginning + /// of this basic block. + /// + /// Offsets are computed assuming worst case padding before an aligned + /// block. This means that subtracting basic block offsets always gives a + /// conservative estimate of the real distance which may be smaller. + /// + /// Because worst case padding is used, the computed offset of an aligned + /// block may not actually be aligned. + unsigned Offset = 0; + + /// Size - Size of the basic block in bytes. If the block contains + /// inline assembly, this is a worst case estimate. + /// + /// The size does not include any alignment padding whether from the + /// beginning of the block, or from an aligned jump table at the end. + unsigned Size = 0; + + BasicBlockInfo() = default; + + unsigned postOffset() const { return Offset + Size; } + }; + + std::vector BBInfo; + + /// WaterList - A sorted list of basic blocks where islands could be placed + /// (i.e. blocks that don't fall through to the following block, due + /// to a return, unreachable, or unconditional branch). + std::vector WaterList; + + /// NewWaterList - The subset of WaterList that was created since the + /// previous iteration by inserting unconditional branches. + SmallSet NewWaterList; + + using water_iterator = std::vector::iterator; + + /// CPUser - One user of a constant pool, keeping the machine instruction + /// pointer, the constant pool being referenced, and the max displacement + /// allowed from the instruction to the CP. The LowWaterMark records the + /// lowest basic block where a new CPEntry can be placed. To ensure this + /// pass terminates, the CP entries are initially placed at the second block + /// of the function and then move monotonically to higher addresses. The + /// exception to this rule is when the current CP entry for a particular + /// CPUser is out of range, but there is another CP entry for the same + /// constant value in range. We want to use the existing in-range CP + /// entry, but if it later moves out of range, the search for new water + /// should resume where it left off. The LowWaterMark is used to record + /// that point. + struct CPUser { + MachineInstr *MI; + MachineInstr *CPEMI; + MachineBasicBlock *LowWaterMark; + + private: + unsigned MaxDisp; + + public: + CPUser(MachineInstr *mi, MachineInstr *cpemi, unsigned maxdisp) + : MI(mi), CPEMI(cpemi), MaxDisp(maxdisp) { + LowWaterMark = CPEMI->getParent(); + } + + /// getMaxDisp - Returns the maximum displacement supported by MI. + unsigned getMaxDisp() const { + unsigned xMaxDisp = + ConstantIslandsSmallOffset ? ConstantIslandsSmallOffset : MaxDisp; + return xMaxDisp; + } + + void setMaxDisp(unsigned val) { MaxDisp = val; } + }; + + /// CPUsers - Keep track of all of the machine instructions that use various + /// constant pools and their max displacement. + std::vector CPUsers; + + /// CPEntry - One per constant pool entry, keeping the machine instruction + /// pointer, the constpool index, and the number of CPUser's which + /// reference this entry. + struct CPEntry { + MachineInstr *CPEMI; + unsigned CPI; + unsigned RefCount; + + CPEntry(MachineInstr *cpemi, unsigned cpi, unsigned rc = 0) + : CPEMI(cpemi), CPI(cpi), RefCount(rc) {} + }; + + /// CPEntries - Keep track of all of the constant pool entry machine + /// instructions. For each original constpool index (i.e. those that + /// existed upon entry to this pass), it keeps a vector of entries. + /// Original elements are cloned as we go along; the clones are + /// put in the vector of the original element, but have distinct CPIs. + std::vector> CPEntries; + + /// ImmBranch - One per immediate branch, keeping the machine instruction + /// pointer, conditional or unconditional, the max displacement, + /// and (if isCond is true) the corresponding unconditional branch + /// opcode. + struct ImmBranch { + MachineInstr *MI; + unsigned MaxDisp : 31; + bool isCond : 1; + int UncondBr; + + ImmBranch(MachineInstr *mi, unsigned maxdisp, bool cond, int ubr) + : MI(mi), MaxDisp(maxdisp), isCond(cond), UncondBr(ubr) {} + }; + + /// ImmBranches - Keep track of all the immediate branch instructions. + /// + std::vector ImmBranches; + + const XtensaSubtarget *STI = nullptr; + const XtensaInstrInfo *TII; + const TargetRegisterInfo *TRI; + XtensaFunctionInfo *MFI; + MachineFunction *MF = nullptr; + MachineConstantPool *MCP = nullptr; + MachineBasicBlock *InitConstantMBB = nullptr; + std::unique_ptr RS; + LivePhysRegs LiveRegs; + + unsigned PICLabelUId; + bool PrescannedForConstants = false; + + void initPICLabelUId(unsigned UId) { PICLabelUId = UId; } + + unsigned createPICLabelUId() { return PICLabelUId++; } + +public: + static char ID; + + XtensaConstantIslands() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return "Xtensa Constant Islands"; } + + bool runOnMachineFunction(MachineFunction &F) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + + void doInitialPlacement(std::vector &CPEMIs); + CPEntry *findConstPoolEntry(unsigned CPI, const MachineInstr *CPEMI); + Align getCPEAlign(const MachineInstr &CPEMI); + void initializeFunctionInfo(const std::vector &CPEMIs); + unsigned getOffsetOf(MachineInstr *MI) const; + unsigned getUserOffset(CPUser &) const; + void dumpBBs(); + + bool isOffsetInRange(unsigned UserOffset, unsigned TrialOffset, + unsigned Disp); + + void computeBlockSize(MachineBasicBlock *MBB); + MachineBasicBlock *splitBlockBeforeInstr(MachineInstr &MI); + void updateForInsertedWaterBlock(MachineBasicBlock *NewBB); + void adjustBBOffsetsAfter(MachineBasicBlock *BB); + bool decrementCPEReferenceCount(unsigned CPI, MachineInstr *CPEMI); + int findInRangeCPEntry(CPUser &U, unsigned UserOffset); + bool findAvailableWater(CPUser &U, unsigned UserOffset, + water_iterator &WaterIter); + void createNewWater(unsigned CPUserIndex, unsigned UserOffset, + MachineBasicBlock *&NewMBB); + bool handleConstantPoolUser(unsigned CPUserIndex); + void removeDeadCPEMI(MachineInstr *CPEMI); + bool removeUnusedCPEntries(); + bool isCPEntryInRange(MachineInstr *MI, unsigned UserOffset, + MachineInstr *CPEMI, unsigned Disp, + bool DoDump = false); + bool isWaterInRange(unsigned UserOffset, MachineBasicBlock *Water, CPUser &U, + unsigned &Growth); + bool isBBInRange(MachineInstr *MI, MachineBasicBlock *BB, unsigned Disp); + bool fixupImmediateBr(ImmBranch &Br); + bool fixupConditionalBr(ImmBranch &Br); + bool fixupUnconditionalBr(ImmBranch &Br); + void removeEntryJump(); +}; + +} // end anonymous namespace + +char XtensaConstantIslands::ID = 0; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +/// print block size and offset information - debugging +LLVM_DUMP_METHOD void XtensaConstantIslands::dumpBBs() { + for (unsigned J = 0, E = BBInfo.size(); J != E; ++J) { + const BasicBlockInfo &BBI = BBInfo[J]; + dbgs() << format("%08x %bb.%u\t", BBI.Offset, J) + << format(" size=%#x\n", BBI.Size); + } +} +#endif + +bool XtensaConstantIslands::runOnMachineFunction(MachineFunction &mf) { + MF = &mf; + MCP = mf.getConstantPool(); + STI = &mf.getSubtarget(); + LLVM_DEBUG(dbgs() << "constant island machine function " + << "\n"); + TII = (const XtensaInstrInfo *)STI->getInstrInfo(); + MFI = MF->getInfo(); + + TRI = STI->getRegisterInfo(); + + if (!STI->useTextSectionLiterals()) + return false; + + if (TRI->trackLivenessAfterRegAlloc(*MF)) + RS.reset(new RegScavenger()); + + LLVM_DEBUG(dbgs() << "constant island processing " + << "\n"); + + // Renumber all of the machine basic blocks in the function, guaranteeing that + // the numbers agree with the position of the block in the function. + MF->RenumberBlocks(); + + bool MadeChange = false; + + // Perform the initial placement of the constant pool entries. To start with, + // we put them all at the end of the function. + std::vector CPEMIs; + doInitialPlacement(CPEMIs); + + // Renumber all of the machine basic blocks in the function, guaranteeing + // that the numbers agree with the position of the block in the function. + MF->RenumberBlocks(); + + /// The next UID to take is the first unused one. + initPICLabelUId(CPEMIs.size()); + + // Do the initial scan of the function, building up information about the + // sizes of each block, the location of all the water, and finding all of the + // constant pool users. + initializeFunctionInfo(CPEMIs); + CPEMIs.clear(); + LLVM_DEBUG(dumpBBs()); + + /// Remove dead constant pool entries. + MadeChange |= removeUnusedCPEntries(); + + // Iteratively place constant pool entries and fix up branches until there + // is no change. + unsigned NoCPIters = 0, NoBRIters = 0; + (void)NoBRIters; + while (true) { + LLVM_DEBUG(dbgs() << "Beginning CP iteration #" << NoCPIters << '\n'); + bool CPChange = false; + for (unsigned i = 0, e = CPUsers.size(); i != e; ++i) { + CPChange |= handleConstantPoolUser(i); + } + if (CPChange && ++NoCPIters > MAX_CP_ITERATIONS) + report_fatal_error("Constant Island pass failed to converge!"); + LLVM_DEBUG(dumpBBs()); + + // Clear NewWaterList now. If we split a block for branches, it should + // appear as "new water" for the next iteration of constant pool placement. + NewWaterList.clear(); + + LLVM_DEBUG(dbgs() << "Beginning BR iteration #" << NoBRIters << '\n'); + bool BRChange = false; + for (unsigned i = 0, e = ImmBranches.size(); i != e; ++i) + BRChange |= fixupImmediateBr(ImmBranches[i]); + if (BRChange && ++NoBRIters > MAX_BR_ITERATIONS) + report_fatal_error("Branch Fix Up pass failed to converge!"); + LLVM_DEBUG(dumpBBs()); + + if (!CPChange && !BRChange) + break; + + MadeChange = true; + } + removeEntryJump(); + LLVM_DEBUG(dbgs() << '\n'; dumpBBs()); + BBInfo.clear(); + WaterList.clear(); + CPUsers.clear(); + CPEntries.clear(); + ImmBranches.clear(); + return MadeChange; +} + +/// BBHasFallthrough - Return true if the specified basic block can fallthrough +/// into the block immediately after it. +static bool BBHasFallthrough(MachineBasicBlock *MBB) { + // Get the next machine basic block in the function. + MachineFunction::iterator MBBI = MBB->getIterator(); + // Can't fall off end of function. + if (std::next(MBBI) == MBB->getParent()->end()) + return false; + + MachineBasicBlock *NextBB = &*std::next(MBBI); + return llvm::is_contained(MBB->successors(), NextBB); +} + +/// doInitialPlacement - Perform the initial placement of the constant pool +/// entries. To start with, we put them all at the end of the function. +void XtensaConstantIslands::doInitialPlacement( + std::vector &CPEMIs) { + // Create the basic block to hold the CPE's. + MachineBasicBlock *BB = MF->CreateMachineBasicBlock(); + + // TODO + MachineBasicBlock *Entry = &MF->front(); + MachineBasicBlock *NewEntry = MF->CreateMachineBasicBlock(); + + MF->insert(Entry->getIterator(), NewEntry); + BuildMI(NewEntry, DebugLoc(), TII->get(Xtensa::J)).addMBB(Entry); + NewEntry->addSuccessor(Entry); + NewEntry->setAlignment(Entry->getAlignment()); + + // Copy live-in information to new block. + for (const MachineBasicBlock::RegisterMaskPair &RegMaskPair : + Entry->liveins()) + NewEntry->addLiveIn(RegMaskPair); + + // MachineConstantPool measures alignment in bytes. We measure in log2(bytes). + const Align MaxAlign = MCP->getConstantPoolAlign(); + + BB->setAlignment(AlignConstantIslands ? MaxAlign : Align(4)); + + MF->insert(Entry->getIterator(), BB); + + // The function needs to be as aligned as the basic blocks. The linker may + // move functions around based on their alignment. + MF->ensureAlignment(BB->getAlignment()); + + // Order the entries in BB by descending alignment. That ensures correct + // alignment of all entries as long as BB is sufficiently aligned. Keep + // track of the insertion point for each alignment. We are going to bucket + // sort the entries as they are created. + SmallVector InsPoint(Log2(MaxAlign) + 1, + BB->end()); + + // Add all of the constants from the constant pool to the end block, use an + // identity mapping of CPI's to CPE's. + const std::vector &CPs = MCP->getConstants(); + + const DataLayout &TD = MF->getDataLayout(); + for (unsigned i = 0, e = CPs.size(); i != e; ++i) { + unsigned Size = CPs[i].getSizeInBytes(TD); + assert(Size >= 4 && "Too small constant pool entry"); + Align Alignment = CPs[i].getAlign(); + // Verify that all constant pool entries are a multiple of their alignment. + // If not, we would have to pad them out so that instructions stay aligned. + assert(isAligned(Alignment, Size) && "CP Entry not multiple of 4 bytes!"); + + // Insert CONSTPOOL_ENTRY before entries with a smaller alignment. + unsigned LogAlign = Log2(Alignment); + MachineBasicBlock::iterator InsAt = InsPoint[LogAlign]; + + MachineInstr *CPEMI = + BuildMI(*BB, InsAt, DebugLoc(), TII->get(Xtensa::CONSTPOOL_ENTRY)) + .addImm(i) + .addConstantPoolIndex(i) + .addImm(Size); + + CPEMIs.push_back(CPEMI); + + // Ensure that future entries with higher alignment get inserted before + // CPEMI. This is bucket sort with iterators. + for (unsigned a = LogAlign + 1; a <= Log2(MaxAlign); ++a) + if (InsPoint[a] == InsAt) + InsPoint[a] = CPEMI; + // Add a new CPEntry, but no corresponding CPUser yet. + CPEntries.emplace_back(1, CPEntry(CPEMI, i)); + ++NumCPEs; + LLVM_DEBUG(dbgs() << "Moved CPI#" << i << " to end of function, size = " + << Size << ", align = " << Alignment.value() << '\n'); + } + InitConstantMBB = BB; + LLVM_DEBUG(BB->dump()); +} + +/// findConstPoolEntry - Given the constpool index and CONSTPOOL_ENTRY MI, +/// look up the corresponding CPEntry. +XtensaConstantIslands::CPEntry * +XtensaConstantIslands::findConstPoolEntry(unsigned CPI, + const MachineInstr *CPEMI) { + std::vector &CPEs = CPEntries[CPI]; + // Number of entries per constpool index should be small, just do a + // linear search. + for (CPEntry &CPE : CPEs) { + if (CPE.CPEMI == CPEMI) + return &CPE; + } + return nullptr; +} + +/// getCPEAlign - Returns the required alignment of the constant pool entry +/// represented by CPEMI. Alignment is measured in log2(bytes) units. +Align XtensaConstantIslands::getCPEAlign(const MachineInstr &CPEMI) { + assert(CPEMI.getOpcode() == Xtensa::CONSTPOOL_ENTRY); + + // Everything is 4-byte aligned unless AlignConstantIslands is set. + if (!AlignConstantIslands) + return Align(4); + + unsigned CPI = CPEMI.getOperand(1).getIndex(); + assert(CPI < MCP->getConstants().size() && "Invalid constant pool index."); + return MCP->getConstants()[CPI].getAlign(); +} + +/// initializeFunctionInfo - Do the initial scan of the function, building up +/// information about the sizes of each block, the location of all the water, +/// and finding all of the constant pool users. +void XtensaConstantIslands::initializeFunctionInfo( + const std::vector &CPEMIs) { + BBInfo.clear(); + BBInfo.resize(MF->getNumBlockIDs()); + + // First thing, compute the size of all basic blocks, and see if the function + // has any inline assembly in it. If so, we have to be conservative about + // alignment assumptions, as we don't know for sure the size of any + // instructions in the inline assembly. + for (MachineBasicBlock &MBB : *MF) + computeBlockSize(&MBB); + + // Compute block offsets. + adjustBBOffsetsAfter(&MF->front()); + + // Now go back through the instructions and build up our data structures. + for (MachineBasicBlock &MBB : *MF) { + // If this block doesn't fall through into the next MBB, then this is + // 'water' that a constant pool island could be placed. + if (!BBHasFallthrough(&MBB)) + WaterList.push_back(&MBB); + for (MachineInstr &MI : MBB) { + if (MI.isDebugInstr()) + continue; + + int Opc = MI.getOpcode(); + if (MI.isBranch()) { + bool isCond = false; + unsigned Bits = 0; + unsigned Scale = 1; + int UOpc = Xtensa::J; + switch (Opc) { + default: + continue; // Ignore other branches for now + case Xtensa::J: + Bits = BITS_JUMP; + Scale = 1; + isCond = false; + break; + case Xtensa::BEQ: + case Xtensa::BNE: + case Xtensa::BLT: + case Xtensa::BLTU: + case Xtensa::BGE: + case Xtensa::BGEU: + case Xtensa::BEQI: + case Xtensa::BNEI: + case Xtensa::BLTI: + case Xtensa::BLTUI: + case Xtensa::BGEI: + case Xtensa::BGEUI: + Bits = 8; + Scale = 1; + isCond = true; + break; + case Xtensa::BEQZ: + case Xtensa::BNEZ: + case Xtensa::BLTZ: + case Xtensa::BGEZ: + Bits = 12; + Scale = 1; + isCond = true; + break; + case Xtensa::BT: + case Xtensa::BF: + Bits = 8; + Scale = 1; + isCond = true; + break; + } + // Record this immediate branch. + unsigned MaxOffs = ((1 << (Bits - 1)) - 1) * Scale - 4; + ImmBranches.push_back(ImmBranch(&MI, MaxOffs, isCond, UOpc)); + } + + if (Opc == Xtensa::CONSTPOOL_ENTRY) + continue; + + // Scan the instructions for constant pool operands. + for (const MachineOperand &MO : MI.operands()) + if (MO.isCPI()) { + // We found one. The addressing mode tells us the max displacement + // from the PC that this instruction permits. + unsigned CPI = MO.getIndex(); + MachineInstr *CPEMI = CPEMIs[CPI]; + + switch (Opc) { + default: + llvm_unreachable("Unknown addressing mode for CP reference!"); + case Xtensa::L32R: + CPUsers.push_back(CPUser(&MI, CPEMI, MAX_DISP_L32R)); + break; + } + + // Increment corresponding CPEntry reference count. + CPEntry *CPE = findConstPoolEntry(CPI, CPEMI); + assert(CPE && "Cannot find a corresponding CPEntry!"); + CPE->RefCount++; + + // Instructions can only use one CP entry, don't bother scanning the + // rest of the operands. + break; + } + } + } +} + +/// computeBlockSize - Compute the size and some alignment information for MBB. +/// This function updates BBInfo directly. +void XtensaConstantIslands::computeBlockSize(MachineBasicBlock *MBB) { + BasicBlockInfo &BBI = BBInfo[MBB->getNumber()]; + BBI.Size = 0; + + for (const MachineInstr &MI : *MBB) { + if (MI.getOpcode() == Xtensa::CONSTPOOL_ENTRY) { + BBI.Size += 4; + } else { + BBI.Size += TII->getInstSizeInBytes(MI); + } + } +} + +/// getOffsetOf - Return the current offset of the specified machine instruction +/// from the start of the function. This offset changes as stuff is moved +/// around inside the function. +unsigned XtensaConstantIslands::getOffsetOf(MachineInstr *MI) const { + MachineBasicBlock *MBB = MI->getParent(); + + // The offset is composed of two things: the sum of the sizes of all MBB's + // before this instruction's block, and the offset from the start of the block + // it is in. + unsigned Offset = BBInfo[MBB->getNumber()].Offset; + + // Sum instructions before MI in MBB. + for (MachineBasicBlock::iterator I = MBB->begin(); &*I != MI; ++I) { + assert(I != MBB->end() && "Didn't find MI in its own basic block?"); + Offset += TII->getInstSizeInBytes(*I); + } + return Offset; +} + +/// CompareMBBNumbers - Little predicate function to sort the WaterList by MBB +/// ID. +static bool CompareMBBNumbers(const MachineBasicBlock *LHS, + const MachineBasicBlock *RHS) { + return LHS->getNumber() < RHS->getNumber(); +} + +/// updateForInsertedWaterBlock - When a block is newly inserted into the +/// machine function, it upsets all of the block numbers. Renumber the blocks +/// and update the arrays that parallel this numbering. +void XtensaConstantIslands::updateForInsertedWaterBlock( + MachineBasicBlock *NewBB) { + // Renumber the MBB's to keep them consecutive. + NewBB->getParent()->RenumberBlocks(NewBB); + + // Insert an entry into BBInfo to align it properly with the (newly + // renumbered) block numbers. + BBInfo.insert(BBInfo.begin() + NewBB->getNumber(), BasicBlockInfo()); + + // Next, update WaterList. Specifically, we need to add NewMBB as having + // available water after it. + water_iterator IP = llvm::lower_bound(WaterList, NewBB, CompareMBBNumbers); + WaterList.insert(IP, NewBB); +} + +unsigned XtensaConstantIslands::getUserOffset(CPUser &U) const { + return getOffsetOf(U.MI); +} + +/// Split the basic block containing MI into two blocks, which are joined by +/// an unconditional branch. Update data structures and renumber blocks to +/// account for this change and returns the newly created block. +MachineBasicBlock * +XtensaConstantIslands::splitBlockBeforeInstr(MachineInstr &MI) { + MachineBasicBlock *OrigBB = MI.getParent(); + + // Collect liveness information at MI. + LivePhysRegs LRs(*MF->getSubtarget().getRegisterInfo()); + LRs.addLiveOuts(*OrigBB); + auto LivenessEnd = ++MachineBasicBlock::iterator(MI).getReverse(); + for (MachineInstr &LiveMI : make_range(OrigBB->rbegin(), LivenessEnd)) + LRs.stepBackward(LiveMI); + + // Create a new MBB for the code after the OrigBB. + MachineBasicBlock *NewBB = + MF->CreateMachineBasicBlock(OrigBB->getBasicBlock()); + MachineFunction::iterator MBBI = ++OrigBB->getIterator(); + MF->insert(MBBI, NewBB); + + // Splice the instructions starting with MI over to NewBB. + NewBB->splice(NewBB->end(), OrigBB, MI, OrigBB->end()); + + // Add an unconditional branch from OrigBB to NewBB. + // Note the new unconditional branch is not being recorded. + // There doesn't seem to be meaningful DebugInfo available; this doesn't + // correspond to anything in the source. + BuildMI(OrigBB, DebugLoc(), TII->get(Xtensa::J)).addMBB(NewBB); + ++NumSplit; + + // Update the CFG. All succs of OrigBB are now succs of NewBB. + NewBB->transferSuccessors(OrigBB); + + // OrigBB branches to NewBB. + OrigBB->addSuccessor(NewBB); + + // Update live-in information in the new block. + MachineRegisterInfo &MRI = MF->getRegInfo(); + for (MCPhysReg L : LRs) + if (!MRI.isReserved(L)) + NewBB->addLiveIn(L); + + // Update internal data structures to account for the newly inserted MBB. + // This is almost the same as updateForInsertedWaterBlock, except that + // the Water goes after OrigBB, not NewBB. + MF->RenumberBlocks(NewBB); + + // Insert an entry into BBInfo to align it properly with the (newly + // renumbered) block numbers. + BBInfo.insert(BBInfo.begin() + NewBB->getNumber(), BasicBlockInfo()); + + // Next, update WaterList. Specifically, we need to add OrigMBB as having + // available water after it (but not if it's already there, which happens + // when splitting before a conditional branch that is followed by an + // unconditional branch - in that case we want to insert NewBB). + water_iterator IP = llvm::lower_bound(WaterList, OrigBB, CompareMBBNumbers); + MachineBasicBlock *WaterBB = *IP; + if (WaterBB == OrigBB) + WaterList.insert(std::next(IP), NewBB); + else + WaterList.insert(IP, OrigBB); + NewWaterList.insert(OrigBB); + + // Figure out how large the OrigBB is. As the first half of the original + // block, it cannot contain a tablejump. The size includes + // the new jump we added. (It should be possible to do this without + // recounting everything, but it's very confusing, and this is rarely + // executed.) + computeBlockSize(OrigBB); + + // Figure out how large the NewMBB is. As the second half of the original + // block, it may contain a tablejump. + computeBlockSize(NewBB); + + // All BBOffsets following these blocks must be modified. + adjustBBOffsetsAfter(OrigBB); + +#if 0 + //TODO + // Need to fix live-in lists if we track liveness. + if (TRI->trackLivenessAfterRegAlloc(*MF)) + computeAndAddLiveIns(LiveRegs, *NewBB); +#endif + return NewBB; +} + +/// isOffsetInRange - Checks whether UserOffset (the location of a constant pool +/// reference) is within MaxDisp of TrialOffset (a proposed location of a +/// constant pool entry). +bool XtensaConstantIslands::isOffsetInRange(unsigned UserOffset, + unsigned TrialOffset, + unsigned MaxDisp) { + UserOffset = (UserOffset + 3) & (~0x3); + if ((UserOffset >= TrialOffset) && (UserOffset - TrialOffset <= MaxDisp)) { + return true; + } + return false; +} + +/// isWaterInRange - Returns true if a CPE placed after the specified +/// Water (a basic block) will be in range for the specific MI. +/// +/// Compute how much the function will grow by inserting a CPE after Water. +bool XtensaConstantIslands::isWaterInRange(unsigned UserOffset, + MachineBasicBlock *Water, CPUser &U, + unsigned &Growth) { + unsigned CPEOffset = BBInfo[Water->getNumber()].postOffset(); + unsigned NextBlockOffset; + Align NextBlockAlignment; + MachineFunction::const_iterator NextBlock = ++Water->getIterator(); + if (NextBlock == MF->end()) { + NextBlockOffset = BBInfo[Water->getNumber()].postOffset(); + NextBlockAlignment = Align(1); + } else { + NextBlockOffset = BBInfo[NextBlock->getNumber()].Offset; + NextBlockAlignment = NextBlock->getAlignment(); + } + unsigned Size = U.CPEMI->getOperand(2).getImm(); + unsigned CPEEnd = CPEOffset + Size; + + // The CPE may be able to hide in the alignment padding before the next + // block. It may also cause more padding to be required if it is more aligned + // that the next block. + if (CPEEnd > NextBlockOffset) { + Growth = CPEEnd - NextBlockOffset; + // Compute the padding that would go at the end of the CPE to align the next + // block. + Growth += offsetToAlignment(CPEEnd, NextBlockAlignment); + + // If the CPE is to be inserted before the instruction, that will raise + // the offset of the instruction. Also account for unknown alignment padding + // in blocks between CPE and the user. + if (CPEOffset < UserOffset) + UserOffset += Growth; + } else + // CPE fits in existing padding. + Growth = 0; + + return isOffsetInRange(UserOffset, CPEOffset, U.getMaxDisp()); +} + +/// isCPEntryInRange - Returns true if the distance between specific MI and +/// specific ConstPool entry instruction can fit in MI's displacement field. +bool XtensaConstantIslands::isCPEntryInRange(MachineInstr *MI, + unsigned UserOffset, + MachineInstr *CPEMI, + unsigned MaxDisp, bool DoDump) { + unsigned CPEOffset = getOffsetOf(CPEMI); + + if (DoDump) { + LLVM_DEBUG({ + unsigned Block = MI->getParent()->getNumber(); + const BasicBlockInfo &BBI = BBInfo[Block]; + dbgs() << "User of CPE#" << CPEMI->getOperand(0).getImm() + << " max delta=" << MaxDisp + << format(" insn address=%#x", UserOffset) << " in " + << printMBBReference(*MI->getParent()) << ": " + << format("%#x-%x\t", BBI.Offset, BBI.postOffset()) << *MI + << format("CPE address=%#x offset=%+d: ", CPEOffset, + int(CPEOffset - UserOffset)); + }); + } + + return isOffsetInRange(UserOffset, CPEOffset, MaxDisp); +} + +#ifndef NDEBUG +/// BBIsJumpedOver - Return true of the specified basic block's only predecessor +/// unconditionally branches to its only successor. +static bool BBIsJumpedOver(MachineBasicBlock *MBB) { + if (MBB->pred_size() != 1 || MBB->succ_size() != 1) + return false; + MachineBasicBlock *Succ = *MBB->succ_begin(); + MachineBasicBlock *Pred = *MBB->pred_begin(); + MachineInstr *PredMI = &Pred->back(); + + if (PredMI->getOpcode() == Xtensa::J) + return PredMI->getOperand(0).getMBB() == Succ; + return false; +} +#endif + +void XtensaConstantIslands::adjustBBOffsetsAfter(MachineBasicBlock *BB) { + unsigned BBNum = BB->getNumber(); + for (unsigned i = BBNum + 1, e = MF->getNumBlockIDs(); i < e; ++i) { + // Get the offset and known bits at the end of the layout predecessor. + // Include the alignment of the current block. + unsigned Offset = BBInfo[i - 1].Offset + BBInfo[i - 1].Size; + Align BlockAlignment = MF->getBlockNumbered(i)->getAlignment(); + BBInfo[i].Offset = Offset + offsetToAlignment(Offset, BlockAlignment); + } +} + +/// decrementCPEReferenceCount - find the constant pool entry with index CPI +/// and instruction CPEMI, and decrement its refcount. If the refcount +/// becomes 0 remove the entry and instruction. Returns true if we removed +/// the entry, false if we didn't. +bool XtensaConstantIslands::decrementCPEReferenceCount(unsigned CPI, + MachineInstr *CPEMI) { + // Find the old entry. Eliminate it if it is no longer used. + CPEntry *CPE = findConstPoolEntry(CPI, CPEMI); + assert(CPE && "Unexpected!"); + if (--CPE->RefCount == 0) { + removeDeadCPEMI(CPEMI); + CPE->CPEMI = nullptr; + --NumCPEs; + return true; + } + return false; +} + +/// LookForCPEntryInRange - see if the currently referenced CPE is in range; +/// if not, see if an in-range clone of the CPE is in range, and if so, +/// change the data structures so the user references the clone. Returns: +/// 0 = no existing entry found +/// 1 = entry found, and there were no code insertions or deletions +/// 2 = entry found, and there were code insertions or deletions +int XtensaConstantIslands::findInRangeCPEntry(CPUser &U, unsigned UserOffset) { + MachineInstr *UserMI = U.MI; + MachineInstr *CPEMI = U.CPEMI; + + // Check to see if the CPE is already in-range. + if (isCPEntryInRange(UserMI, UserOffset, CPEMI, U.getMaxDisp())) { + LLVM_DEBUG(dbgs() << "In range\n"); + return 1; + } + + // No. Look for previously created clones of the CPE that are in range. + unsigned CPI = CPEMI->getOperand(1).getIndex(); + std::vector &CPEs = CPEntries[CPI]; + for (CPEntry &CPE : CPEs) { + // We already tried this one + if (CPE.CPEMI == CPEMI) + continue; + // Removing CPEs can leave empty entries, skip + if (CPE.CPEMI == nullptr) + continue; + if (isCPEntryInRange(UserMI, UserOffset, CPE.CPEMI, U.getMaxDisp())) { + LLVM_DEBUG(dbgs() << "Replacing CPE#" << CPI << " with CPE#" << CPE.CPI + << "\n"); + // Point the CPUser node to the replacement + U.CPEMI = CPE.CPEMI; + // Change the CPI in the instruction operand to refer to the clone. + for (MachineOperand &MO : UserMI->operands()) + if (MO.isCPI()) { + MO.setIndex(CPE.CPI); + break; + } + // Adjust the refcount of the clone... + CPE.RefCount++; + // ...and the original. If we didn't remove the old entry, none of the + // addresses changed, so we don't need another pass. + return decrementCPEReferenceCount(CPI, CPEMI) ? 2 : 1; + } + } + return 0; +} + +/// getUnconditionalBrDisp - Returns the maximum displacement that can fit in +/// the specific unconditional branch instruction. +static inline unsigned getUnconditionalBrDisp(int Opc) { + // Currently only J instruction is used + return (1 << (BITS_JUMP - 1)); +} + +/// findAvailableWater - Look for an existing entry in the WaterList in which +/// we can place the CPE referenced from U so it's within range of U's MI. +/// Returns true if found, false if not. If it returns true, WaterIter +/// is set to the WaterList entry. +/// To ensure that this pass +/// terminates, the CPE location for a particular CPUser is only allowed to +/// move to a lower address, so search backward from the end of the list and +/// prefer the first water that is in range. +bool XtensaConstantIslands::findAvailableWater(CPUser &U, unsigned UserOffset, + water_iterator &WaterIter) { + if (WaterList.empty()) + return false; + + unsigned BestGrowth = ~0u; + for (water_iterator IP = std::prev(WaterList.end()), B = WaterList.begin();; + --IP) { + MachineBasicBlock *WaterBB = *IP; + // Check if water is in range and is either at a higher address than the + // current "low water mark" or a new water block that was created since + // the previous iteration by inserting an unconditional branch. In the + // latter case, we want to allow resetting the low water mark back to + // this new water since we haven't seen it before. Inserting branches + // should be relatively uncommon and when it does happen, we want to be + // sure to take advantage of it for all the CPEs near that block, so that + // we don't insert more branches than necessary. + unsigned Growth; + if (isWaterInRange(UserOffset, WaterBB, U, Growth) && + (WaterBB->getNumber() > U.LowWaterMark->getNumber() || + NewWaterList.count(WaterBB)) && + Growth < BestGrowth) { + // This is the least amount of required padding seen so far. + BestGrowth = Growth; + WaterIter = IP; + LLVM_DEBUG(dbgs() << "Found water after " << printMBBReference(*WaterBB) + << " Growth=" << Growth << '\n'); + + // Keep looking unless it is perfect. + if (BestGrowth == 0) + return true; + } + if (IP == B) + break; + } + return BestGrowth != ~0u; +} + +/// createNewWater - No existing WaterList entry will work for +/// CPUsers[CPUserIndex], so create a place to put the CPE. The end of the +/// block is used if in range, and the conditional branch munged so control +/// flow is correct. Otherwise the block is split to create a hole with an +/// unconditional branch around it. In either case NewMBB is set to a +/// block following which the new island can be inserted (the WaterList +/// is not adjusted). +void XtensaConstantIslands::createNewWater(unsigned CPUserIndex, + unsigned UserOffset, + MachineBasicBlock *&NewMBB) { + CPUser &U = CPUsers[CPUserIndex]; + MachineInstr *UserMI = U.MI; + MachineBasicBlock *UserMBB = UserMI->getParent(); + NewMBB = splitBlockBeforeInstr(*UserMI); +} + +/// handleConstantPoolUser - Analyze the specified user, checking to see if it +/// is out-of-range. If so, pick up the constant pool value and move it some +/// place in-range. Return true if we changed any addresses (thus must run +/// another pass of branch lengthening), false otherwise. +bool XtensaConstantIslands::handleConstantPoolUser(unsigned CPUserIndex) { + CPUser &U = CPUsers[CPUserIndex]; + MachineInstr *UserMI = U.MI; + MachineInstr *CPEMI = U.CPEMI; + unsigned CPI = CPEMI->getOperand(1).getIndex(); + unsigned Size = CPEMI->getOperand(2).getImm(); + // Compute this only once, it's expensive. + unsigned UserOffset = getUserOffset(U); + + // See if the current entry is within range, or there is a clone of it + // in range. + int result = findInRangeCPEntry(U, UserOffset); + if (result == 1) + return false; + else if (result == 2) + return true; + + // Look for water where we can place this CPE. + MachineBasicBlock *NewIsland = MF->CreateMachineBasicBlock(); + MachineBasicBlock *NewMBB; + water_iterator IP; + if (findAvailableWater(U, UserOffset, IP)) { + LLVM_DEBUG(dbgs() << "Found water in range\n"); + MachineBasicBlock *WaterBB = *IP; + + // If the original WaterList entry was "new water" on this iteration, + // propagate that to the new island. This is just keeping NewWaterList + // updated to match the WaterList, which will be updated below. + if (NewWaterList.erase(WaterBB)) + NewWaterList.insert(NewIsland); + + // The new CPE goes before the following block (NewMBB). + NewMBB = &*++WaterBB->getIterator(); + } else { + // No water found. + LLVM_DEBUG(dbgs() << "No water found\n"); + createNewWater(CPUserIndex, UserOffset, NewMBB); + + // splitBlockBeforeInstr adds to WaterList, which is important when it is + // called while handling branches so that the water will be seen on the + // next iteration for constant pools, but in this context, we don't want + // it. Check for this so it will be removed from the WaterList. + // Also remove any entry from NewWaterList. + MachineBasicBlock *WaterBB = &*--NewMBB->getIterator(); + IP = llvm::find(WaterList, WaterBB); + if (IP != WaterList.end()) + NewWaterList.erase(WaterBB); + + // We are adding new water. Update NewWaterList. + NewWaterList.insert(NewIsland); + } + + // Remove the original WaterList entry; we want subsequent insertions in + // this vicinity to go after the one we're about to insert. This + // considerably reduces the number of times we have to move the same CPE + // more than once and is also important to ensure the algorithm terminates. + if (IP != WaterList.end()) + WaterList.erase(IP); + + // Okay, we know we can put an island before NewMBB now, do it! + MF->insert(NewMBB->getIterator(), NewIsland); + + // Update internal data structures to account for the newly inserted MBB. + updateForInsertedWaterBlock(NewIsland); + + // Decrement the old entry, and remove it if refcount becomes 0. + decrementCPEReferenceCount(CPI, CPEMI); + + // No existing clone of this CPE is within range. + // We will be generating a new clone. Get a UID for it. + unsigned ID = createPICLabelUId(); + + // Now that we have an island to add the CPE to, clone the original CPE and + // add it to the island. + U.LowWaterMark = NewIsland; + U.CPEMI = BuildMI(NewIsland, DebugLoc(), TII->get(Xtensa::CONSTPOOL_ENTRY)) + .addImm(ID) + .addConstantPoolIndex(CPI) + .addImm(Size); + CPEntries[CPI].push_back(CPEntry(U.CPEMI, ID, 1)); + ++NumCPEs; + + // Mark the basic block as aligned as required by the const-pool entry. + NewIsland->setAlignment(getCPEAlign(*U.CPEMI)); + + // Increase the size of the island block to account for the new entry. + BBInfo[NewIsland->getNumber()].Size += Size; + adjustBBOffsetsAfter(&*--NewIsland->getIterator()); + + // Finally, change the CPI in the instruction operand to be ID. + for (MachineOperand &MO : UserMI->operands()) + if (MO.isCPI()) { + MO.setIndex(ID); + break; + } + + LLVM_DEBUG( + dbgs() << " Moved CPE to #" << ID << " CPI=" << CPI + << format(" offset=%#x\n", BBInfo[NewIsland->getNumber()].Offset)); + + return true; +} + +/// removeDeadCPEMI - Remove a dead constant pool entry instruction. Update +/// sizes and offsets of impacted basic blocks. +void XtensaConstantIslands::removeDeadCPEMI(MachineInstr *CPEMI) { + MachineBasicBlock *CPEBB = CPEMI->getParent(); + unsigned Size = CPEMI->getOperand(2).getImm(); + CPEMI->eraseFromParent(); + BBInfo[CPEBB->getNumber()].Size -= Size; + // All succeeding offsets have the current size value added in, fix this. + if (CPEBB->empty()) { + BBInfo[CPEBB->getNumber()].Size = 0; + + // This block no longer needs to be aligned. + CPEBB->setAlignment(Align(1)); + } else { + // Entries are sorted by descending alignment, so realign from the front. + CPEBB->setAlignment(getCPEAlign(*CPEBB->begin())); + } + + adjustBBOffsetsAfter(CPEBB); + // An island has only one predecessor BB and one successor BB. Check if + // this BB's predecessor jumps directly to this BB's successor. This + // shouldn't happen currently. + assert(!BBIsJumpedOver(CPEBB) && "How did this happen?"); + // FIXME: remove the empty blocks after all the work is done? +} + +/// removeUnusedCPEntries - Remove constant pool entries whose refcounts +/// are zero. +bool XtensaConstantIslands::removeUnusedCPEntries() { + unsigned MadeChange = false; + for (std::vector &CPEs : CPEntries) { + for (CPEntry &CPE : CPEs) { + if (CPE.RefCount == 0 && CPE.CPEMI) { + removeDeadCPEMI(CPE.CPEMI); + CPE.CPEMI = nullptr; + MadeChange = true; + } + } + } + return MadeChange; +} + +/// isBBInRange - Returns true if the distance between specific MI and +/// specific BB can fit in MI's displacement field. +bool XtensaConstantIslands::isBBInRange(MachineInstr *MI, + MachineBasicBlock *DestBB, + unsigned MaxDisp) { + unsigned PCAdj = 4; + unsigned BrOffset = getOffsetOf(MI) + PCAdj; + unsigned DestOffset = BBInfo[DestBB->getNumber()].Offset; + + LLVM_DEBUG(dbgs() << "Branch of destination " << printMBBReference(*DestBB) + << " from " << printMBBReference(*MI->getParent()) + << " max delta=" << MaxDisp << " from " << getOffsetOf(MI) + << " to " << DestOffset << " offset " + << int(DestOffset - BrOffset) << "\t" << *MI); + if (BrOffset <= DestOffset) { + // Branch before the Dest. + if (DestOffset - BrOffset < MaxDisp) + return true; + } else { + if (BrOffset - DestOffset <= MaxDisp) + return true; + } + return false; +} + +/// fixupImmediateBr - Fix up an immediate branch whose destination is too far +/// away to fit in its displacement field. +bool XtensaConstantIslands::fixupImmediateBr(ImmBranch &Br) { + MachineInstr *MI = Br.MI; + + if (MI->getOpcode() == Xtensa::JX) + return false; + + // TOOO: currently we don't fix J in start block + if (MI->getParent()->getNumber() == 0) + return false; + + unsigned TargetOperand = branchTargetOperand(MI); + MachineBasicBlock *DestBB = MI->getOperand(TargetOperand).getMBB(); + + // Check to see if the DestBB is already in-range. + if (isBBInRange(MI, DestBB, Br.MaxDisp)) + return false; + + if (!Br.isCond) + return fixupUnconditionalBr(Br); + return fixupConditionalBr(Br); +} + +/// fixupUnconditionalBr - Fix up an unconditional branch whose destination is +/// too far away to fit in its displacement field. If the LR register has been +/// spilled in the epilogue, then we can use BL to implement a far jump. +/// Otherwise, add an intermediate branch instruction to a branch. +/// fixupUnconditionalBr - Fix up an unconditional branch whose destination is +/// too far away to fit in its displacement field. If the LR register has been +/// spilled in the epilogue, then we can use BSR to implement a far jump. +/// Otherwise, add an intermediate branch instruction to a branch. +bool XtensaConstantIslands::fixupUnconditionalBr(ImmBranch &Br) { + MachineInstr *MI = Br.MI; + MachineBasicBlock *MBB = MI->getParent(); + MachineBasicBlock *DestBB = TII->getBranchDestBlock(*MI); + MachineFunction *MF = MBB->getParent(); + MachineRegisterInfo &MRI = MF->getRegInfo(); + MachineConstantPool *ConstantPool = MF->getConstantPool(); + + XtensaConstantPoolValue *C = + XtensaConstantPoolMBB::Create(MF->getFunction().getContext(), DestBB, 0); + unsigned CPSize = ConstantPool->getConstants().size(); + unsigned Idx = ConstantPool->getConstantPoolIndex(C, Align(4)); + Register DestReg = MRI.createVirtualRegister(&Xtensa::ARRegClass); + + MachineInstr *CPEMI = nullptr; + // Add a new CPEntry, but no corresponding CPUser yet. + unsigned ID = Idx; + if (CPSize == Idx) { + ID = createPICLabelUId(); + CPEMI = + BuildMI(InitConstantMBB, DebugLoc(), TII->get(Xtensa::CONSTPOOL_ENTRY)) + .addImm(ID) + .addConstantPoolIndex(Idx) + .addImm(4); + CPEntries.emplace_back(1, CPEntry(CPEMI, Idx, 1)); + ++NumCPEs; + BBInfo[InitConstantMBB->getNumber()].Size += 4; + adjustBBOffsetsAfter(InitConstantMBB); + } else { + std::vector &CPEs = CPEntries[Idx]; + for (CPEntry &CPE : CPEs) { + if ((CPE.CPEMI != nullptr) && + isCPEntryInRange(MI, MAX_DISP_L32R, CPE.CPEMI, MAX_DISP_L32R, true)) { + CPEMI = CPE.CPEMI; + CPE.RefCount++; + } + } + if (CPEMI == nullptr) { + ID = createPICLabelUId(); + CPEMI = BuildMI(InitConstantMBB, DebugLoc(), + TII->get(Xtensa::CONSTPOOL_ENTRY)) + .addImm(ID) + .addConstantPoolIndex(Idx) + .addImm(4); + CPEntries[Idx].push_back(CPEntry(CPEMI, ID, 1)); + ++NumCPEs; + BBInfo[InitConstantMBB->getNumber()].Size += 4; + adjustBBOffsetsAfter(InitConstantMBB); + } else { + ID = CPEMI->getOperand(0).getImm(); + } + } + MachineInstr *L32R = + BuildMI(*MBB, MI, DebugLoc(), TII->get(Xtensa::L32R), DestReg) + .addConstantPoolIndex(ID); + + MI->setDesc(TII->get(Xtensa::JX)); + MI->removeOperand(0); + MI->addOperand(MachineOperand::CreateReg(DestReg, true)); + + RegScavenger RS; + RS.enterBasicBlockEnd(*MBB); + unsigned Scav = RS.scavengeRegisterBackwards(Xtensa::ARRegClass, + L32R->getIterator(), false, 0); + MRI.replaceRegWith(DestReg, Scav); + MRI.clearVirtRegs(); + RS.setRegUsed(Scav); + CPUsers.push_back(CPUser(L32R, CPEMI, MAX_DISP_L32R)); + BBInfo[MBB->getNumber()].Size += 3; + adjustBBOffsetsAfter(MBB); + ++NumUBrFixed; + + LLVM_DEBUG(dbgs() << " Changed B to long jump " << *MI); + return true; +} + +// TODO +/// fixupConditionalBr - Fix up a conditional branch whose destination is too +/// far away to fit in its displacement field. It is converted to an inverse +/// conditional branch + an unconditional branch to the destination. +bool XtensaConstantIslands::fixupConditionalBr(ImmBranch &Br) { + MachineInstr *MI = Br.MI; + MachineBasicBlock *DestBB = TII->getBranchDestBlock(*MI); + + SmallVector Cond; + Cond.push_back(MachineOperand::CreateImm(MI->getOpcode())); + Cond.push_back(MI->getOperand(0)); + TII->reverseBranchCondition(Cond); + // Add an unconditional branch to the destination and invert the branch + // condition to jump over it: + // bteqz L1 + // => + // bnez L2 + // b L1 + // L2: + + // If the branch is at the end of its MBB and that has a fall-through block, + // direct the updated conditional branch to the fall-through block. Otherwise, + // split the MBB before the next instruction. + MachineBasicBlock *MBB = MI->getParent(); + MachineInstr *BMI = &MBB->back(); + bool NeedSplit = (BMI != MI) || !BBHasFallthrough(MBB); + + ++NumCBrFixed; + if (BMI != MI) { + if (std::next(MachineBasicBlock::iterator(MI)) == std::prev(MBB->end()) && + BMI->isUnconditionalBranch()) { + // Last MI in the BB is an unconditional branch. Can we simply invert the + // condition and swap destinations: + // beqz L1 + // b L2 + // => + // bnez L2 + // b L1 + MachineBasicBlock *NewDest = TII->getBranchDestBlock(*BMI); + if (isBBInRange(MI, NewDest, Br.MaxDisp)) { + LLVM_DEBUG( + dbgs() << " Invert Bcc condition and swap its destination with " + << *BMI); + BMI->getOperand(BMI->getNumExplicitOperands() - 1).setMBB(DestBB); + MI->getOperand(MI->getNumExplicitOperands() - 1).setMBB(NewDest); + + MI->setDesc(TII->get(Cond[0].getImm())); + return true; + } + } + } + + if (NeedSplit) { + splitBlockBeforeInstr(*MI); + // No need for the branch to the next block. We're adding an unconditional + // branch to the destination. + int Delta = TII->getInstSizeInBytes(MBB->back()); + BBInfo[MBB->getNumber()].Size -= Delta; + MBB->back().eraseFromParent(); + + // The conditional successor will be swapped between the BBs after this, so + // update CFG. + MBB->addSuccessor(DestBB); + std::next(MBB->getIterator())->removeSuccessor(DestBB); + } + MachineBasicBlock *NextBB = &*++MBB->getIterator(); + + LLVM_DEBUG(dbgs() << " Insert B to " << printMBBReference(*DestBB) + << " also invert condition and change dest. to " + << printMBBReference(*NextBB) << "\n"); + + // Insert a new conditional branch and a new unconditional branch. + // Also update the ImmBranch as well as adding a new entry for the new branch. + switch (MI->getOpcode()) { + case Xtensa::BEQ: + case Xtensa::BNE: + case Xtensa::BLT: + case Xtensa::BLTU: + case Xtensa::BGE: + case Xtensa::BGEU: + BuildMI(MBB, DebugLoc(), TII->get(Cond[0].getImm())) + .addReg(MI->getOperand(0).getReg()) + .addReg(MI->getOperand(1).getReg()) + .addMBB(NextBB); + break; + case Xtensa::BEQI: + case Xtensa::BNEI: + case Xtensa::BLTI: + case Xtensa::BLTUI: + case Xtensa::BGEI: + case Xtensa::BGEUI: + BuildMI(MBB, DebugLoc(), TII->get(Cond[0].getImm())) + .addReg(MI->getOperand(0).getReg()) + .addImm(MI->getOperand(1).getImm()) + .addMBB(NextBB); + break; + case Xtensa::BEQZ: + case Xtensa::BNEZ: + case Xtensa::BLTZ: + case Xtensa::BGEZ: + BuildMI(MBB, DebugLoc(), TII->get(Cond[0].getImm())) + .addReg(MI->getOperand(0).getReg()) + .addMBB(NextBB); + break; + case Xtensa::BT: + case Xtensa::BF: + BuildMI(MBB, DebugLoc(), TII->get(Cond[0].getImm())) + .addReg(MI->getOperand(0).getReg()) + .addMBB(NextBB); + break; + } + + Br.MI = &MBB->back(); + BBInfo[MBB->getNumber()].Size += TII->getInstSizeInBytes(MBB->back()); + BuildMI(MBB, DebugLoc(), TII->get(Br.UncondBr)).addMBB(DestBB); + BBInfo[MBB->getNumber()].Size += TII->getInstSizeInBytes(MBB->back()); + unsigned MaxDisp = getUnconditionalBrDisp(Br.UncondBr); + ImmBranches.push_back(ImmBranch(&MBB->back(), MaxDisp, false, Br.UncondBr)); + + // Remove the old conditional branch. It may or may not still be in MBB. + BBInfo[MI->getParent()->getNumber()].Size -= TII->getInstSizeInBytes(*MI); + MI->eraseFromParent(); + adjustBBOffsetsAfter(MBB); + return true; +} + +// Check first constant island. If it is empty, then we can remove first block, +// which contains jump instruction to the third block and first constant +// insland. +void XtensaConstantIslands::removeEntryJump() { + MachineFunction *MF = InitConstantMBB->getParent(); + MachineBasicBlock *Entry = &MF->front(); + if (InitConstantMBB->empty()) { + Entry->removeSuccessor(Entry->getSingleSuccessor()); + MF->remove(Entry); + MF->remove(InitConstantMBB); + MF->RenumberBlocks(); + } +} + +/// Returns a pass that converts branches to long branches. +FunctionPass *llvm::createXtensaConstantIslandPass() { + return new XtensaConstantIslands(); +} diff --git a/llvm/lib/Target/Xtensa/XtensaConstantPoolValue.cpp b/llvm/lib/Target/Xtensa/XtensaConstantPoolValue.cpp new file mode 100644 index 0000000000000..b7502eabbf716 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaConstantPoolValue.cpp @@ -0,0 +1,234 @@ +//===- XtensaConstantPoolValue.cpp - Xtensa constantpool value ------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the Xtensa specific constantpool value class. +// +//===----------------------------------------------------------------------===// + +#include "XtensaConstantPoolValue.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/raw_ostream.h" +#include +using namespace llvm; + +XtensaConstantPoolValue::XtensaConstantPoolValue( + Type *Ty, unsigned id, XtensaCP::XtensaCPKind kind, bool addCurrentAddress, + XtensaCP::XtensaCPModifier modifier) + : MachineConstantPoolValue(Ty), LabelId(id), Kind(kind), Modifier(modifier), + AddCurrentAddress(addCurrentAddress) {} + +XtensaConstantPoolValue::XtensaConstantPoolValue( + LLVMContext &C, unsigned id, XtensaCP::XtensaCPKind kind, + bool addCurrentAddress, XtensaCP::XtensaCPModifier modifier) + : MachineConstantPoolValue((Type *)Type::getInt32Ty(C)), LabelId(id), + Kind(kind), Modifier(modifier), AddCurrentAddress(addCurrentAddress) {} + +XtensaConstantPoolValue::~XtensaConstantPoolValue() {} + +StringRef XtensaConstantPoolValue::getModifierText() const { + switch (Modifier) { + case XtensaCP::no_modifier: + return ""; + case XtensaCP::TPOFF: + return "@TPOFF"; + } + llvm_unreachable("Unknown modifier!"); +} + +int XtensaConstantPoolValue::getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) { + llvm_unreachable("Shouldn't be calling this directly!"); +} + +void XtensaConstantPoolValue::addSelectionDAGCSEId(FoldingSetNodeID &ID) { + ID.AddInteger(LabelId); +} + +bool XtensaConstantPoolValue::hasSameValue(XtensaConstantPoolValue *ACPV) { + if (ACPV->Kind == Kind) { + if (ACPV->LabelId == LabelId) + return true; + // Two PC relative constpool entries containing the same GV address or + // external symbols. FIXME: What about blockaddress? + if (Kind == XtensaCP::CPValue || Kind == XtensaCP::CPExtSymbol) + return true; + } + return false; +} + +void XtensaConstantPoolValue::dump() const { errs() << " " << *this; } + +void XtensaConstantPoolValue::print(raw_ostream &O) const {} + +//===----------------------------------------------------------------------===// +// XtensaConstantPoolConstant +//===----------------------------------------------------------------------===// + +XtensaConstantPoolConstant::XtensaConstantPoolConstant( + Type *Ty, const Constant *C, unsigned ID, XtensaCP::XtensaCPKind Kind, + bool AddCurrentAddress) + : XtensaConstantPoolValue((Type *)C->getType(), ID, Kind, + AddCurrentAddress), + CVal(C) {} + +XtensaConstantPoolConstant::XtensaConstantPoolConstant( + const Constant *C, unsigned ID, XtensaCP::XtensaCPKind Kind, + bool AddCurrentAddress) + : XtensaConstantPoolValue((Type *)C->getType(), ID, Kind, + AddCurrentAddress), + CVal(C) {} + +XtensaConstantPoolConstant * +XtensaConstantPoolConstant::Create(const Constant *C, unsigned ID, + XtensaCP::XtensaCPKind Kind) { + return new XtensaConstantPoolConstant(C, ID, Kind, false); +} + +XtensaConstantPoolConstant * +XtensaConstantPoolConstant::Create(const Constant *C, unsigned ID, + XtensaCP::XtensaCPKind Kind, + bool AddCurrentAddress) { + return new XtensaConstantPoolConstant(C, ID, Kind, AddCurrentAddress); +} + +const GlobalValue *XtensaConstantPoolConstant::getGV() const { + return dyn_cast_or_null(CVal); +} + +const BlockAddress *XtensaConstantPoolConstant::getBlockAddress() const { + return dyn_cast_or_null(CVal); +} + +int XtensaConstantPoolConstant::getExistingMachineCPValue( + MachineConstantPool *CP, Align Alignment) { + return getExistingMachineCPValueImpl(CP, + Alignment); +} + +bool XtensaConstantPoolConstant::hasSameValue(XtensaConstantPoolValue *ACPV) { + const XtensaConstantPoolConstant *ACPC = + dyn_cast(ACPV); + return ACPC && ACPC->CVal == CVal && + XtensaConstantPoolValue::hasSameValue(ACPV); +} + +void XtensaConstantPoolConstant::addSelectionDAGCSEId(FoldingSetNodeID &ID) { + ID.AddPointer(CVal); + XtensaConstantPoolValue::addSelectionDAGCSEId(ID); +} + +void XtensaConstantPoolConstant::print(raw_ostream &O) const { + O << CVal->getName(); + XtensaConstantPoolValue::print(O); +} + +XtensaConstantPoolSymbol::XtensaConstantPoolSymbol( + LLVMContext &C, const char *s, unsigned id, bool AddCurrentAddress, + bool PrivLinkage, XtensaCP::XtensaCPModifier Modifier) + : XtensaConstantPoolValue(C, id, XtensaCP::CPExtSymbol, AddCurrentAddress, + Modifier), + S(s), PrivateLinkage(PrivLinkage) {} + +XtensaConstantPoolSymbol * +XtensaConstantPoolSymbol::Create(LLVMContext &C, const char *s, unsigned ID, + bool PrivLinkage, + XtensaCP::XtensaCPModifier Modifier) + +{ + return new XtensaConstantPoolSymbol(C, s, ID, false, PrivLinkage, Modifier); +} + +int XtensaConstantPoolSymbol::getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) { + return getExistingMachineCPValueImpl(CP, Alignment); +} + +bool XtensaConstantPoolSymbol::hasSameValue(XtensaConstantPoolValue *ACPV) { + const XtensaConstantPoolSymbol *ACPS = + dyn_cast(ACPV); + return ACPS && ACPS->S == S && XtensaConstantPoolValue::hasSameValue(ACPV); +} + +void XtensaConstantPoolSymbol::addSelectionDAGCSEId(FoldingSetNodeID &ID) { + ID.AddString(S); + XtensaConstantPoolValue::addSelectionDAGCSEId(ID); +} + +void XtensaConstantPoolSymbol::print(raw_ostream &O) const { + O << S; + XtensaConstantPoolValue::print(O); +} + +XtensaConstantPoolMBB::XtensaConstantPoolMBB(LLVMContext &C, + const MachineBasicBlock *mbb, + unsigned id) + : XtensaConstantPoolValue(C, 0, XtensaCP::CPMachineBasicBlock, false), + MBB(mbb) {} + +XtensaConstantPoolMBB * +XtensaConstantPoolMBB::Create(LLVMContext &C, const MachineBasicBlock *mbb, + unsigned idx) { + return new XtensaConstantPoolMBB(C, mbb, idx); +} + +int XtensaConstantPoolMBB::getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) { + return getExistingMachineCPValueImpl(CP, Alignment); +} + +bool XtensaConstantPoolMBB::hasSameValue(XtensaConstantPoolValue *ACPV) { + const XtensaConstantPoolMBB *ACPMBB = dyn_cast(ACPV); + return ACPMBB && ACPMBB->MBB == MBB && + XtensaConstantPoolValue::hasSameValue(ACPV); +} + +void XtensaConstantPoolMBB::addSelectionDAGCSEId(FoldingSetNodeID &ID) { + ID.AddPointer(MBB); + XtensaConstantPoolValue::addSelectionDAGCSEId(ID); +} + +void XtensaConstantPoolMBB::print(raw_ostream &O) const { + O << "BB#" << MBB->getNumber(); + XtensaConstantPoolValue::print(O); +} + +XtensaConstantPoolJumpTable::XtensaConstantPoolJumpTable(LLVMContext &C, + unsigned idx) + : XtensaConstantPoolValue(C, 0, XtensaCP::CPJumpTable, false), IDX(idx) {} + +XtensaConstantPoolJumpTable *XtensaConstantPoolJumpTable::Create(LLVMContext &C, + unsigned idx) { + return new XtensaConstantPoolJumpTable(C, idx); +} + +int XtensaConstantPoolJumpTable::getExistingMachineCPValue( + MachineConstantPool *CP, Align Alignment) { + return getExistingMachineCPValueImpl(CP, + Alignment); +} + +bool XtensaConstantPoolJumpTable::hasSameValue(XtensaConstantPoolValue *ACPV) { + const XtensaConstantPoolJumpTable *ACPJT = + dyn_cast(ACPV); + return ACPJT && ACPJT->IDX == IDX && + XtensaConstantPoolValue::hasSameValue(ACPV); +} + +void XtensaConstantPoolJumpTable::addSelectionDAGCSEId(FoldingSetNodeID &ID) {} + +void XtensaConstantPoolJumpTable::print(raw_ostream &O) const { + O << "JT" << IDX; + XtensaConstantPoolValue::print(O); +} diff --git a/llvm/lib/Target/Xtensa/XtensaConstantPoolValue.h b/llvm/lib/Target/Xtensa/XtensaConstantPoolValue.h new file mode 100644 index 0000000000000..094394e5b70e0 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaConstantPoolValue.h @@ -0,0 +1,280 @@ +//===- XtensaConstantPoolValue.h - Xtensa constantpool value ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the Xtensa specific constantpool value class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSACONSTANTPOOLVALUE_H +#define LLVM_LIB_TARGET_XTENSA_XTENSACONSTANTPOOLVALUE_H + +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include + +namespace llvm { + +class BlockAddress; +class Constant; +class GlobalValue; +class LLVMContext; +class MachineBasicBlock; + +namespace XtensaCP { +enum XtensaCPKind { + CPValue, + CPExtSymbol, + CPBlockAddress, + CPMachineBasicBlock, + CPJumpTable +}; + +enum XtensaCPModifier { + no_modifier, // None + TPOFF // Thread Pointer Offset +}; +} // namespace XtensaCP + +/// XtensaConstantPoolValue - Xtensa specific constantpool value. This is used +/// to represent PC-relative displacement between the address of the load +/// instruction and the constant being loaded, i.e. (&GV-(LPIC+8)). +class XtensaConstantPoolValue : public MachineConstantPoolValue { + unsigned LabelId; // Label id of the load. + XtensaCP::XtensaCPKind Kind; // Kind of constant. + XtensaCP::XtensaCPModifier Modifier; // GV modifier + bool AddCurrentAddress; + +protected: + XtensaConstantPoolValue( + Type *Ty, unsigned id, XtensaCP::XtensaCPKind Kind, + bool AddCurrentAddress, + XtensaCP::XtensaCPModifier Modifier = XtensaCP::no_modifier); + + XtensaConstantPoolValue( + LLVMContext &C, unsigned id, XtensaCP::XtensaCPKind Kind, + bool AddCurrentAddress, + XtensaCP::XtensaCPModifier Modifier = XtensaCP::no_modifier); + + template + int getExistingMachineCPValueImpl(MachineConstantPool *CP, + Align Alignment) { + const std::vector &Constants = CP->getConstants(); + for (unsigned i = 0, e = Constants.size(); i != e; ++i) { + if (Constants[i].isMachineConstantPoolEntry() && + (Constants[i].getAlign() >= Alignment)) { + XtensaConstantPoolValue *CPV = + (XtensaConstantPoolValue *)Constants[i].Val.MachineCPVal; + if (Derived *APC = dyn_cast(CPV)) + if (cast(this)->equals(APC)) + return i; + } + } + + return -1; + } + +public: + ~XtensaConstantPoolValue() override; + + XtensaCP::XtensaCPModifier getModifier() const { return Modifier; } + bool hasModifier() const { return Modifier != XtensaCP::no_modifier; } + StringRef getModifierText() const; + + bool mustAddCurrentAddress() const { return AddCurrentAddress; } + + unsigned getLabelId() const { return LabelId; } + void setLabelId(unsigned id) { LabelId = id; } + + bool isGlobalValue() const { return Kind == XtensaCP::CPValue; } + bool isExtSymbol() const { return Kind == XtensaCP::CPExtSymbol; } + bool isBlockAddress() const { return Kind == XtensaCP::CPBlockAddress; } + bool isMachineBasicBlock() const { + return Kind == XtensaCP::CPMachineBasicBlock; + } + bool isJumpTable() const { return Kind == XtensaCP::CPJumpTable; } + + int getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) override; + + void addSelectionDAGCSEId(FoldingSetNodeID &ID) override; + + /// hasSameValue - Return true if this Xtensa constpool value can share the + /// same constantpool entry as another Xtensa constpool value. + virtual bool hasSameValue(XtensaConstantPoolValue *ACPV); + + bool equals(const XtensaConstantPoolValue *A) const { + return this->LabelId == A->LabelId && this->Modifier == A->Modifier; + } + + void print(raw_ostream &O) const override; + void print(raw_ostream *O) const { + if (O) + print(*O); + } + void dump() const; +}; + +inline raw_ostream &operator<<(raw_ostream &O, + const XtensaConstantPoolValue &V) { + V.print(O); + return O; +} + +/// XtensaConstantPoolConstant - Xtensa-specific constant pool values for +/// Constants, Functions, and BlockAddresses. +class XtensaConstantPoolConstant : public XtensaConstantPoolValue { + const Constant *CVal; // Constant being loaded. + + XtensaConstantPoolConstant(const Constant *C, unsigned ID, + XtensaCP::XtensaCPKind Kind, + bool AddCurrentAddress); + XtensaConstantPoolConstant(Type *Ty, const Constant *C, unsigned ID, + XtensaCP::XtensaCPKind Kind, + bool AddCurrentAddress); + +public: + static XtensaConstantPoolConstant *Create(const Constant *C, unsigned ID, + XtensaCP::XtensaCPKind Kind); + static XtensaConstantPoolConstant *Create(const Constant *C, unsigned ID, + XtensaCP::XtensaCPKind Kind, + bool AddCurrentAddress); + + const GlobalValue *getGV() const; + const BlockAddress *getBlockAddress() const; + + int getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) override; + + /// hasSameValue - Return true if this Xtensa constpool value can share the + /// same constantpool entry as another Xtensa constpool value. + bool hasSameValue(XtensaConstantPoolValue *ACPV) override; + + void addSelectionDAGCSEId(FoldingSetNodeID &ID) override; + + void print(raw_ostream &O) const override; + static bool classof(const XtensaConstantPoolValue *APV) { + return APV->isGlobalValue() || APV->isBlockAddress(); + } + + bool equals(const XtensaConstantPoolConstant *A) const { + return CVal == A->CVal && XtensaConstantPoolValue::equals(A); + } +}; + +/// XtensaConstantPoolSymbol - Xtensa-specific constantpool values for external +/// symbols. +class XtensaConstantPoolSymbol : public XtensaConstantPoolValue { + const std::string S; // ExtSymbol being loaded. + bool PrivateLinkage; + + XtensaConstantPoolSymbol( + LLVMContext &C, const char *s, unsigned id, bool AddCurrentAddress, + bool PrivLinkage, + XtensaCP::XtensaCPModifier Modifier = XtensaCP::no_modifier); + +public: + static XtensaConstantPoolSymbol * + Create(LLVMContext &C, const char *s, unsigned ID, bool PrivLinkage, + XtensaCP::XtensaCPModifier Modifier = XtensaCP::no_modifier); + + const char *getSymbol() const { return S.c_str(); } + + int getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) override; + + void addSelectionDAGCSEId(FoldingSetNodeID &ID) override; + + /// hasSameValue - Return true if this Xtensa constpool value can share the + /// same constantpool entry as another Xtensa constpool value. + bool hasSameValue(XtensaConstantPoolValue *ACPV) override; + + bool isPrivateLinkage() { return PrivateLinkage; } + + void print(raw_ostream &O) const override; + + static bool classof(const XtensaConstantPoolValue *ACPV) { + return ACPV->isExtSymbol(); + } + + bool equals(const XtensaConstantPoolSymbol *A) const { + return S == A->S && XtensaConstantPoolValue::equals(A); + } +}; + +/// XtensaConstantPoolMBB - Xtensa-specific constantpool value of a machine +/// basic block. +class XtensaConstantPoolMBB : public XtensaConstantPoolValue { + const MachineBasicBlock *MBB; // Machine basic block. + + XtensaConstantPoolMBB(LLVMContext &C, const MachineBasicBlock *mbb, + unsigned id); + +public: + static XtensaConstantPoolMBB * + Create(LLVMContext &C, const MachineBasicBlock *mbb, unsigned ID); + + const MachineBasicBlock *getMBB() const { return MBB; } + + int getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) override; + + void addSelectionDAGCSEId(FoldingSetNodeID &ID) override; + + /// hasSameValue - Return true if this Xtensa constpool value can share the + /// same constantpool entry as another Xtensa constpool value. + bool hasSameValue(XtensaConstantPoolValue *ACPV) override; + + void print(raw_ostream &O) const override; + + static bool classof(const XtensaConstantPoolValue *ACPV) { + return ACPV->isMachineBasicBlock(); + } + + bool equals(const XtensaConstantPoolMBB *A) const { + return MBB == A->MBB && XtensaConstantPoolValue::equals(A); + } +}; + +/// XtensaConstantPoolJumpTable - Xtensa-specific constantpool values for Jump +/// Table symbols. +class XtensaConstantPoolJumpTable : public XtensaConstantPoolValue { + unsigned IDX; // Jump Table Index. + + XtensaConstantPoolJumpTable(LLVMContext &C, unsigned idx); + +public: + static XtensaConstantPoolJumpTable *Create(LLVMContext &C, unsigned idx); + + unsigned getIndex() const { return IDX; } + + int getExistingMachineCPValue(MachineConstantPool *CP, + Align Alignment) override; + + void addSelectionDAGCSEId(FoldingSetNodeID &ID) override; + + /// hasSameValue - Return true if this Xtensa constpool value can share the + /// same constantpool entry as another Xtensa constpool value. + bool hasSameValue(XtensaConstantPoolValue *ACPV) override; + + void print(raw_ostream &O) const override; + + static bool classof(const XtensaConstantPoolValue *ACPV) { + return ACPV->isJumpTable(); + } + + bool equals(const XtensaConstantPoolJumpTable *A) const { + return IDX == A->IDX && XtensaConstantPoolValue::equals(A); + } +}; + +} // namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_XTENSACONSTANTPOOLVALUE_H */ diff --git a/llvm/lib/Target/Xtensa/XtensaDSPInstrInfo.td b/llvm/lib/Target/Xtensa/XtensaDSPInstrInfo.td new file mode 100644 index 0000000000000..aec66efd8d2f2 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaDSPInstrInfo.td @@ -0,0 +1,472 @@ +//===- XtensaDSPInstrInfo.td - Xtensa Target Description ---*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file describes the Xtensa DSP instructions in TableGen format. +// +//===----------------------------------------------------------------------===// + +// Multiply +class UMUL_AA oper1, string instrAsm, SDPatternOperator opNode> + : RRR_Inst<0x04, oper1, 0x07, (outs), (ins AR:$s, AR:$t), + instrAsm#"\t$s, $t", + [(opNode AR:$s, AR:$t)]>, Requires<[HasMAC16]> { + let r = 0; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def UMUL_AA_LL : UMUL_AA<0x00, "umul.aa.ll", int_xtensa_umul_aa_ll>; +def UMUL_AA_HL : UMUL_AA<0x01, "umul.aa.hl", int_xtensa_umul_aa_hl>; +def UMUL_AA_LH : UMUL_AA<0x02, "umul.aa.lh", int_xtensa_umul_aa_lh>; +def UMUL_AA_HH : UMUL_AA<0x03, "umul.aa.hh", int_xtensa_umul_aa_hh>; + +class MUL_AA oper1, string instrAsm, SDPatternOperator opNode> + : RRR_Inst<0x04, oper1, 0x07, (outs), (ins AR:$s, AR:$t), + instrAsm#"\t$s, $t", + [(opNode AR:$s, AR:$t)]>, Requires<[HasMAC16]> { + let r = 0; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MUL_AA_LL : MUL_AA<0x04, "mul.aa.ll", int_xtensa_mul_aa_ll>; +def MUL_AA_HL : MUL_AA<0x05, "mul.aa.hl", int_xtensa_mul_aa_hl>; +def MUL_AA_LH : MUL_AA<0x06, "mul.aa.lh", int_xtensa_mul_aa_lh>; +def MUL_AA_HH : MUL_AA<0x07, "mul.aa.hh", int_xtensa_mul_aa_hh>; + +class MUL_AD oper1, string instrAsm> + : RRR_Inst<0x04, oper1, 0x03, (outs), (ins AR:$s, MR23:$y), + instrAsm#"\t$s, $y", []>, Requires<[HasMAC16]> { + bits<2> y; + + let r = 0; + let t{3} = 0; + let t{2} = y{0}; + let t{1-0} = 0; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MUL_AD_LL : MUL_AD<0x04, "mul.ad.ll">; +def MUL_AD_HL : MUL_AD<0x05, "mul.ad.hl">; +def MUL_AD_LH : MUL_AD<0x06, "mul.ad.lh">; +def MUL_AD_HH : MUL_AD<0x07, "mul.ad.hh">; + +class MUL_DA oper1, string instrAsm> + : RRR_Inst<0x04, oper1, 0x06, (outs), (ins MR01:$x, AR:$t), + instrAsm#"\t$x, $t", []>, Requires<[HasMAC16]> { + bits<2> x; + + let r{3} = 0; + let r{2} = x{0}; + let r{1-0} = 0; + let s = 0; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MUL_DA_LL : MUL_DA<0x04, "mul.da.ll">; +def MUL_DA_HL : MUL_DA<0x05, "mul.da.hl">; +def MUL_DA_LH : MUL_DA<0x06, "mul.da.lh">; +def MUL_DA_HH : MUL_DA<0x07, "mul.da.hh">; + +class MUL_DD oper1, string instrAsm> + : RRR_Inst<0x04, oper1, 0x02, (outs), (ins MR01:$x, MR23:$y), + instrAsm#"\t$x, $y", []>, Requires<[HasMAC16]> { + bits<2> x; + bits<2> y; + + let r{3} = 0; + let r{2} = x{0}; + let r{1-0} = 0; + let s = 0; + let t{3} = 0; + let t{2} = y{0}; + let t{1-0} = 0; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MUL_DD_LL : MUL_DD<0x04, "mul.dd.ll">; +def MUL_DD_HL : MUL_DD<0x05, "mul.dd.hl">; +def MUL_DD_LH : MUL_DD<0x06, "mul.dd.lh">; +def MUL_DD_HH : MUL_DD<0x07, "mul.dd.hh">; + +class MULA_AA oper1, string instrAsm, SDPatternOperator opNode> + : RRR_Inst<0x04, oper1, 0x07, (outs), (ins AR:$s, AR:$t), + instrAsm#"\t$s, $t", + [(opNode AR:$s, AR:$t)]>, Requires<[HasMAC16]> { + let r = 0; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MULA_AA_LL : MULA_AA<0x08, "mula.aa.ll", int_xtensa_mula_aa_ll>; +def MULA_AA_HL : MULA_AA<0x09, "mula.aa.hl", int_xtensa_mula_aa_hl>; +def MULA_AA_LH : MULA_AA<0x0A, "mula.aa.lh", int_xtensa_mula_aa_lh>; +def MULA_AA_HH : MULA_AA<0x0B, "mula.aa.hh", int_xtensa_mula_aa_hh>; + +class MULA_AD oper1, string instrAsm> + : RRR_Inst<0x04, oper1, 0x03, (outs), (ins AR:$s, MR23:$y), + instrAsm#"\t$s, $y", []>, Requires<[HasMAC16]> { + bits<2> y; + + let r = 0; + let t{3} = 0; + let t{2} = y{0}; + let t{1-0} = 0; + + let Uses = [ACCLO, ACCHI]; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MULA_AD_LL : MULA_AD<0x08, "mula.ad.ll">; +def MULA_AD_HL : MULA_AD<0x09, "mula.ad.hl">; +def MULA_AD_LH : MULA_AD<0x0A, "mula.ad.lh">; +def MULA_AD_HH : MULA_AD<0x0B, "mula.ad.hh">; + +class MULA_DA oper1, string instrAsm> + : RRR_Inst<0x04, oper1, 0x06, (outs), (ins MR01:$x, AR:$t), + instrAsm#"\t$x, $t", []>, Requires<[HasMAC16]> { + bits<2> x; + + let r{3} = 0; + let r{2} = x{0}; + let r{1-0} = 0; + let s = 0; + + let Uses = [ACCLO, ACCHI]; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MULA_DA_LL : MULA_DA<0x08, "mula.da.ll">; +def MULA_DA_HL : MULA_DA<0x09, "mula.da.hl">; +def MULA_DA_LH : MULA_DA<0x0A, "mula.da.lh">; +def MULA_DA_HH : MULA_DA<0x0B, "mula.da.hh">; + +class MULA_DD oper1, string instrAsm> + : RRR_Inst<0x04, oper1, 0x02, (outs), (ins MR01:$x, MR23:$y), + instrAsm#"\t$x, $y", []>, Requires<[HasMAC16]> { + bits<2> x; + bits<2> y; + + let r{3} = 0; + let r{2} = x{0}; + let r{1-0} = 0; + let s = 0; + let t{3} = 0; + let t{2} = y{0}; + let t{1-0} = 0; + + let Uses = [ACCLO, ACCHI]; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MULA_DD_LL : MULA_DD<0x08, "mula.dd.ll">; +def MULA_DD_HL : MULA_DD<0x09, "mula.dd.hl">; +def MULA_DD_LH : MULA_DD<0x0A, "mula.dd.lh">; +def MULA_DD_HH : MULA_DD<0x0B, "mula.dd.hh">; + +class MULS_AA oper1, string instrAsm, SDPatternOperator opNode> + : RRR_Inst<0x04, oper1, 0x07, (outs), (ins AR:$s, AR:$t), + instrAsm#"\t$s, $t", + [(opNode AR:$s, AR:$t)]>, Requires<[HasMAC16]> { + let r = 0; + let Uses = [ACCLO, ACCHI]; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MULS_AA_LL : MULS_AA<0x0C, "muls.aa.ll", int_xtensa_muls_aa_ll>; +def MULS_AA_HL : MULS_AA<0x0D, "muls.aa.hl", int_xtensa_muls_aa_hl>; +def MULS_AA_LH : MULS_AA<0x0E, "muls.aa.lh", int_xtensa_muls_aa_lh>; +def MULS_AA_HH : MULS_AA<0x0F, "muls.aa.hh", int_xtensa_muls_aa_hh>; + +class MULS_AD oper1, string instrAsm> + : RRR_Inst<0x04, oper1, 0x03, (outs), (ins AR:$s, MR23:$y), + instrAsm#"\t$s, $y", []>, Requires<[HasMAC16]> { + bits<2> y; + + let r = 0; + let t{3} = 0; + let t{2} = y{0}; + let t{1-0} = 0; + + let Uses = [ACCLO, ACCHI]; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MULS_AD_LL : MULS_AD<0x0C, "muls.ad.ll">; +def MULS_AD_HL : MULS_AD<0x0D, "muls.ad.hl">; +def MULS_AD_LH : MULS_AD<0x0E, "muls.ad.lh">; +def MULS_AD_HH : MULS_AD<0x0F, "muls.ad.hh">; + +class MULS_DA oper1, string instrAsm> + : RRR_Inst<0x04, oper1, 0x06, (outs), (ins MR01:$x, AR:$t), + instrAsm#"\t$x, $t", []>, Requires<[HasMAC16]> { + bits<2> x; + + let r{3} = 0; + let r{2} = x{0}; + let r{1-0} = 0; + let s = 0; + + let Uses = [ACCLO, ACCHI]; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MULS_DA_LL : MULS_DA<0x0C, "muls.da.ll">; +def MULS_DA_HL : MULS_DA<0x0D, "muls.da.hl">; +def MULS_DA_LH : MULS_DA<0x0E, "muls.da.lh">; +def MULS_DA_HH : MULS_DA<0x0F, "muls.da.hh">; + +class MULS_DD oper1, string instrAsm> + : RRR_Inst<0x04, oper1, 0x02, (outs), (ins MR01:$x, MR23:$y), + instrAsm#"\t$x, $y", []>, Requires<[HasMAC16]> { + bits<2> x; + bits<2> y; + + let r{3} = 0; + let r{2} = x{0}; + let r{1-0} = 0; + let s = 0; + let t{3} = 0; + let t{2} = y{0}; + let t{1-0} = 0; + + let Uses = [ACCLO, ACCHI]; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MULS_DD_LL : MULS_DD<0x0C, "muls.dd.ll">; +def MULS_DD_HL : MULS_DD<0x0D, "muls.dd.hl">; +def MULS_DD_LH : MULS_DD<0x0E, "muls.dd.lh">; +def MULS_DD_HH : MULS_DD<0x0F, "muls.dd.hh">; + +//===----------------------------------------------------------------------===// +// Multiply-accumulate with load + +class MULA_DA_LDDEC oper1, string instrAsm> + : RRR_Inst<0x04, oper1, 0x05, (outs MR:$w, AR:$d), (ins AR:$s, MR01:$x, AR:$t), + instrAsm#"\t $w, $s, $x, $t", []>, Requires<[HasMAC16]> { + bits<2> x; + bits<2> w; + + let Constraints = "$s = $d"; + let mayLoad = 1; + let r{3} = 0; + let r{2} = x{0}; + let r{1-0} = w{1-0}; + let Uses = [ACCLO, ACCHI]; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MULA_DA_LL_LDDEC : MULA_DA_LDDEC<0x08, "mula.da.ll.lddec">; +def MULA_DA_HL_LDDEC : MULA_DA_LDDEC<0x09, "mula.da.hl.lddec">; +def MULA_DA_LH_LDDEC : MULA_DA_LDDEC<0x0A, "mula.da.lh.lddec">; +def MULA_DA_HH_LDDEC : MULA_DA_LDDEC<0x0B, "mula.da.hh.lddec">; + +let usesCustomInserter = 1, Predicates = [HasMAC16] in { + def MULA_DA_LL_LDDEC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, AR:$t), + "!xtensa_mula_da_ll_lddec_p, $mw, $s, $mx, $t", + [(int_xtensa_mula_da_ll_lddec timm:$mw, AR:$s, timm:$mx, AR:$t)]>; + def MULA_DA_HL_LDDEC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, AR:$t), + "!xtensa_mula_da_hl_lddec_p, $mw, $s, $mx, $t", + [(int_xtensa_mula_da_hl_lddec timm:$mw, AR:$s, timm:$mx, AR:$t)]>; + def MULA_DA_LH_LDDEC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, AR:$t), + "!xtensa_mula_da_lh_lddec_p, $mw, $s, $mx, $t", + [(int_xtensa_mula_da_lh_lddec timm:$mw, AR:$s, timm:$mx, AR:$t)]>; + def MULA_DA_HH_LDDEC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, AR:$t), + "!xtensa_mula_da_hh_lddec_p, $mw, $s, $mx, $t", + [(int_xtensa_mula_da_hh_lddec timm:$mw, AR:$s, timm:$mx, AR:$t)]>; +} + +class MULA_DA_LDINC oper1, string instrAsm> + : RRR_Inst<0x04, oper1, 0x04, (outs MR:$w, AR:$d), (ins AR:$s, MR:$x, AR:$t), + instrAsm#"\t $w, $s, $x, $t", []>, Requires<[HasMAC16]> { + bits<1> x; + bits<2> w; + + let Constraints = "$s = $d"; + let mayLoad = 1; + let r{3} = 0; + let r{2} = x{0}; + let r{1-0} = w{1-0}; + let Uses = [ACCLO, ACCHI]; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MULA_DA_LL_LDINC : MULA_DA_LDINC<0x08, "mula.da.ll.ldinc">; +def MULA_DA_HL_LDINC : MULA_DA_LDINC<0x09, "mula.da.hl.ldinc">; +def MULA_DA_LH_LDINC : MULA_DA_LDINC<0x0A, "mula.da.lh.ldinc">; +def MULA_DA_HH_LDINC : MULA_DA_LDINC<0x0B, "mula.da.hh.ldinc">; + +let usesCustomInserter = 1, Predicates = [HasMAC16] in { + def MULA_DA_LL_LDINC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, AR:$t), + "!xtensa_mula_da_ll_ldinc_p, $mw, $s, $mx, $t", + [(int_xtensa_mula_da_ll_ldinc timm:$mw, AR:$s, timm:$mx, AR:$t)]>; + def MULA_DA_HL_LDINC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, AR:$t), + "!xtensa_mula_da_hl_ldinc_p, $mw, $s, $mx, $t", + [(int_xtensa_mula_da_hl_ldinc timm:$mw, AR:$s, timm:$mx, AR:$t)]>; + def MULA_DA_LH_LDINC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, AR:$t), + "!xtensa_mula_da_lh_ldinc_p, $mw, $s, $mx, $t", + [(int_xtensa_mula_da_lh_ldinc timm:$mw, AR:$s, timm:$mx, AR:$t)]>; + def MULA_DA_HH_LDINC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, AR:$t), + "!xtensa_mula_da_hh_ldinc_p, $mw, $s, $mx, $t", + [(int_xtensa_mula_da_hh_ldinc timm:$mw, AR:$s, timm:$mx, AR:$t)]>; +} + +class MULA_DD_LDDEC oper1, string instrAsm> + : RRR_Inst<0x04, oper1, 0x01, (outs MR:$w, AR:$d), (ins AR:$s, MR01:$x, MR23:$y), + instrAsm#"\t $w, $s, $x, $y", []>, Requires<[HasMAC16]> { + bits<2> x; + bits<2> y; + bits<2> w; + + let Constraints = "$s = $d"; + let mayLoad = 1; + let r{3} = 0; + let r{2} = x{0}; + let r{1-0} = w{1-0}; + let t{3} = 0; + let t{2} = y{0}; + let t{1-0} = 0; + let Uses = [ACCLO, ACCHI]; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MULA_DD_LL_LDDEC : MULA_DD_LDDEC<0x08, "mula.dd.ll.lddec">; +def MULA_DD_HL_LDDEC : MULA_DD_LDDEC<0x09, "mula.dd.hl.lddec">; +def MULA_DD_LH_LDDEC : MULA_DD_LDDEC<0x0A, "mula.dd.lh.lddec">; +def MULA_DD_HH_LDDEC : MULA_DD_LDDEC<0x0B, "mula.dd.hh.lddec">; + +let usesCustomInserter = 1, Predicates = [HasMAC16] in { + def MULA_DD_LL_LDDEC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, imm8:$my), + "!xtensa_mula_dd_ll_lddec_p, $mw, $s, $mx, $my", + [(int_xtensa_mula_dd_ll_lddec timm:$mw, AR:$s, timm:$mx, timm:$my)]>; + def MULA_DD_HL_LDDEC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, imm8:$my), + "!xtensa_mula_dd_hl_lddec_p, $mw, $s, $mx, $my", + [(int_xtensa_mula_dd_hl_lddec timm:$mw, AR:$s, timm:$mx, timm:$my)]>; + def MULA_DD_LH_LDDEC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, imm8:$my), + "!xtensa_mula_dd_lh_lddec_p, $mw, $s, $mx, $my", + [(int_xtensa_mula_dd_lh_lddec timm:$mw, AR:$s, timm:$mx, timm:$my)]>; + def MULA_DD_HH_LDDEC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, imm8:$my), + "!xtensa_mula_dd_hh_lddec_p, $mw, $s, $mx, $my", + [(int_xtensa_mula_dd_hh_lddec timm:$mw, AR:$s, timm:$mx, timm:$my)]>; +} + +class MULA_DD_LDINC oper1, string instrAsm> + : RRR_Inst<0x04, oper1, 0x00, (outs MR:$w, AR:$d), (ins AR:$s, MR01:$x, MR23:$y), + instrAsm#"\t $w, $s, $x, $y", []>, Requires<[HasMAC16]> { + bits<2> x; + bits<2> y; + bits<2> w; + + let Constraints = "$s = $d"; + let mayLoad = 1; + let r{3} = 0; + let r{2} = x{0}; + let r{1-0} = w{1-0}; + let t{3} = 0; + let t{2} = y{0}; + let t{1-0} = 0; + let Uses = [ACCLO, ACCHI]; + let Defs = [M1, M2, ACCLO, ACCHI]; +} + +def MULA_DD_LL_LDINC : MULA_DD_LDINC<0x08, "mula.dd.ll.ldinc">; +def MULA_DD_HL_LDINC : MULA_DD_LDINC<0x09, "mula.dd.hl.ldinc">; +def MULA_DD_LH_LDINC : MULA_DD_LDINC<0x0A, "mula.dd.lh.ldinc">; +def MULA_DD_HH_LDINC : MULA_DD_LDINC<0x0B, "mula.dd.hh.ldinc">; + +let usesCustomInserter = 1, Predicates = [HasMAC16] in { + def MULA_DD_LL_LDINC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, imm8:$my), + "!xtensa_mula_dd_ll_ldinc_p, $mw, $s, $mx, $my", + [(int_xtensa_mula_dd_ll_ldinc timm:$mw, AR:$s, timm:$mx, timm:$my)]>; + def MULA_DD_HL_LDINC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, imm8:$my), + "!xtensa_mula_dd_hl_ldinc_p, $mw, $s, $mx, $my", + [(int_xtensa_mula_dd_hl_ldinc timm:$mw, AR:$s, timm:$mx, timm:$my)]>; + def MULA_DD_LH_LDINC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, imm8:$my), + "!xtensa_mula_dd_lh_ldinc_p, $mw, $s, $mx, $my", + [(int_xtensa_mula_dd_lh_ldinc timm:$mw, AR:$s, timm:$mx, timm:$my)]>; + def MULA_DD_HH_LDINC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s, imm8:$mx, imm8:$my), + "!xtensa_mula_dd_hh_ldinc_p, $mw, $s, $mx, $my", + [(int_xtensa_mula_dd_hh_ldinc timm:$mw, AR:$s, timm:$mx, timm:$my)]>; +} + +def LDDEC : RRR_Inst<0x04, 0x00, 0x09, (outs MR:$w, AR:$d), (ins AR:$s), + "lddec\t $w, $s", []>, Requires<[HasMAC16]> { + bits<2> w; + + let Constraints = "$s = $d"; + let mayLoad = 1; + let r{3-2} = 0; + let r{1-0} = w{1-0}; + let t = 0x00; +} + +def LDINC : RRR_Inst<0x04, 0x00, 0x08, (outs MR:$w, AR:$d), (ins AR:$s), + "ldinc\t $w, $s", []>, Requires<[HasMAC16]> { + bits<2> w; + + let Constraints = "$s = $d"; + let mayLoad = 1; + let r{3-2} = 0; + let r{1-0} = w{1-0}; + let t = 0; +} + +let usesCustomInserter = 1, Predicates = [HasMAC16] in { + def LDDEC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s), + "!xtensa_lddec_p, $mw, $s", + [(int_xtensa_lddec timm:$mw, AR:$s)]>; + def LDINC_P : Pseudo<(outs), (ins imm8:$mw, AR:$s), + "!xtensa_ldinc_p, $mw, $s", + [(int_xtensa_ldinc timm:$mw, AR:$s)]>; +} + +def : Pat<(i32 (int_xtensa_rsr_acclo)), (RSR ACCLO)>; +def : Pat<(i32 (int_xtensa_rsr_acchi)), (RSR ACCHI)>; +def : Pat<(i32 (int_xtensa_rsr_m0)), (RSR M0)>; +def : Pat<(i32 (int_xtensa_rsr_m1)), (RSR M1)>; +def : Pat<(i32 (int_xtensa_rsr_m2)), (RSR M2)>; +def : Pat<(i32 (int_xtensa_rsr_m3)), (RSR M3)>; + +let usesCustomInserter = 1, Predicates = [HasMAC16] in { + def XSR_ACCLO_P : Pseudo<(outs), (ins AR:$s), + "!xtensa_xsr_acclo_p, $s", + [(int_xtensa_xsr_acclo AR:$s)]>; + def XSR_ACCHI_P : Pseudo<(outs), (ins AR:$s), + "!xtensa_xsr_acchi_p, $s", + [(int_xtensa_xsr_acchi AR:$s)]>; + def XSR_M0_P : Pseudo<(outs), (ins AR:$s), + "!xtensa_xsr_m0_p, $s", + [(int_xtensa_xsr_m0 AR:$s)]>; + def XSR_M1_P : Pseudo<(outs), (ins AR:$s), + "!xtensa_xsr_m1_p, $s", + [(int_xtensa_xsr_m1 AR:$s)]>; + def XSR_M2_P : Pseudo<(outs), (ins AR:$s), + "!xtensa_xsr_m2_p, $s", + [(int_xtensa_xsr_m2 AR:$s)]>; + def XSR_M3_P : Pseudo<(outs), (ins AR:$s), + "!xtensa_xsr_m3_p, $s", + [(int_xtensa_xsr_m3 AR:$s)]>; + def WSR_ACCLO_P : Pseudo<(outs), (ins AR:$s), + "!xtensa_wsr_acclo_p, $s", + [(int_xtensa_wsr_acclo AR:$s)]>; + def WSR_ACCHI_P : Pseudo<(outs), (ins AR:$s), + "!xtensa_wsr_acchi_p, $s", + [(int_xtensa_wsr_acchi AR:$s)]>; + def WSR_M0_P : Pseudo<(outs), (ins AR:$s), + "!xtensa_wsr_m0_p, $s", + [(int_xtensa_wsr_m0 AR:$s)]>; + def WSR_M1_P : Pseudo<(outs), (ins AR:$s), + "!xtensa_wsr_m1_p, $s", + [(int_xtensa_wsr_m1 AR:$s)]>; + def WSR_M2_P : Pseudo<(outs), (ins AR:$s), + "!xtensa_wsr_m2_p, $s", + [(int_xtensa_wsr_m2 AR:$s)]>; + def WSR_M3_P : Pseudo<(outs), (ins AR:$s), + "!xtensa_wsr_m3_p, $s", + [(int_xtensa_wsr_m3 AR:$s)]>; +} diff --git a/llvm/lib/Target/Xtensa/XtensaESP32PSRAMFix.cpp b/llvm/lib/Target/Xtensa/XtensaESP32PSRAMFix.cpp new file mode 100644 index 0000000000000..0c4433ed00122 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaESP32PSRAMFix.cpp @@ -0,0 +1,361 @@ +//===- XtensaPSRAMFIx.cpp - Fixup PSRAM Cache issues --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Xtensa.h" +#include "XtensaInstrInfo.h" +#include "XtensaSubtarget.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen//MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Target/TargetMachine.h" + +using namespace llvm; + +#define DEBUG_TYPE "xtensa-fix-esp32-psram-cache-pass" + +enum PSRAMFixChoice { + ESP32_PSRAM_FIX_MEMW, + ESP32_PSRAM_FIX_NOPS +}; + +static cl::opt AlwaysMembarrier("malways-memw", cl::init(false), + cl::Hidden); + +static cl::opt FixESP32PSRAMCacheIssue("mfix-esp32-psram-cache-issue", + cl::init(false), cl::Hidden); + +static cl::opt ESP32PSRAMFixStrat( + "mfix-esp32-psram-cache-strategy", cl::init(ESP32_PSRAM_FIX_MEMW), + cl::desc(""), + cl::values(clEnumValN(ESP32_PSRAM_FIX_MEMW, "memw", ""), + clEnumValN(ESP32_PSRAM_FIX_NOPS, "nops", ""))); + +STATISTIC(NumAdded, "Number of instructions added"); + +class createXtensaPSRAMCacheFix : public MachineFunctionPass { +public: + static char ID; + createXtensaPSRAMCacheFix() : MachineFunctionPass(ID) {} + + const XtensaSubtarget *Subtarget; + static const XtensaInstrInfo *XtensaII; + + bool runOnMachineFunction(MachineFunction &MF) override; + + llvm::StringRef getPassName() const override { + return "Xtensa fix PSRAM cache issue in the ESP32 chips"; + } + +private: + bool xtensaPSRAMCacheFixNopReorg(MachineFunction &MF); + /* + Alternative fix to xtensaPSRAMCacheFixNopReorg. Tries to solve the 32-bit + load/store inversion by explicitly inserting a memory barrier instead of nops. + Slower than nops, but faster than just adding memws everywhere. + */ + bool xtensaPSRAMCacheFixMemwReorg(MachineFunction &MF); + // Emits a memw before every load/store instruction. Hard-handed approach to + // get rid of any pipeline/memory issues... + bool xtensaInsertMemwReorg(MachineFunction &MF); +}; + +char createXtensaPSRAMCacheFix::ID = 0; +const XtensaInstrInfo *createXtensaPSRAMCacheFix::XtensaII; + +// Affected piece of pipeline is 5 entries long; the load/store itself fills +// one. +#define LOAD_STORE_OFF 4 + +bool createXtensaPSRAMCacheFix::xtensaPSRAMCacheFixNopReorg( + MachineFunction &MF) { + MachineFunction::iterator I = MF.begin(), E = MF.end(); + MachineInstr *LastHIQIStore = nullptr; + MachineInstr *StoreInsn = nullptr; + int InsnsSinceStore = 0; + bool Modified = false; + + for (; I != E; ++I) { + MachineBasicBlock &MBB = *I; + MachineBasicBlock::instr_iterator MII = MBB.instr_begin(), + MIE = MBB.instr_end(); + MachineBasicBlock::instr_iterator NextMII; + + // Iterate through the instructions in the basic block + for (; MII != MIE; MII = NextMII) { + MachineInstr *MI = &*MII; + unsigned Opcode = MI->getOpcode(); + NextMII = std::next(MII); + + if (MI->isCall() || MI->isBranch() || MI->isReturn()) { + if (LastHIQIStore) { + DebugLoc dl = LastHIQIStore->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::MEMW); + BuildMI(*LastHIQIStore->getParent(), LastHIQIStore, dl, NewMCID); + LastHIQIStore = nullptr; + Modified = true; + NumAdded++; + } + if (!(MI->isBranch() && (MI->getOpcode() != Xtensa::J) && + (MI->getOpcode() != Xtensa::JX))) { + StoreInsn = nullptr; + } + continue; + } + + switch (Opcode) { + case Xtensa::LSI: + case Xtensa::L32I_N: + case Xtensa::L32I: + case Xtensa::L16SI: + case Xtensa::L16UI: + case Xtensa::L8UI: + if (StoreInsn) { + while (InsnsSinceStore++ < LOAD_STORE_OFF) { + DebugLoc dl = MII->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::NOP); + BuildMI(MBB, MII, dl, NewMCID); + Modified = true; + NumAdded++; + } + } + if (LastHIQIStore) { + DebugLoc dl = LastHIQIStore->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::MEMW); + BuildMI(*LastHIQIStore->getParent(), + std::next(LastHIQIStore->getIterator()), dl, NewMCID); + LastHIQIStore = nullptr; + Modified = true; + NumAdded++; + } + break; + case Xtensa::SSI: + case Xtensa::S32I_N: + case Xtensa::S32I: { + LastHIQIStore = nullptr; + InsnsSinceStore = 0; + StoreInsn = MI; + } break; + case Xtensa::S16I: + case Xtensa::S8I: { + LastHIQIStore = MI; + InsnsSinceStore = 0; + StoreInsn = MI; + } break; + default: + InsnsSinceStore++; + break; + } + } + } + return Modified; +} + +bool createXtensaPSRAMCacheFix::xtensaPSRAMCacheFixMemwReorg( + MachineFunction &MF) { + MachineFunction::iterator I = MF.begin(), E = MF.end(); + MachineInstr *LastHIQIStore = nullptr; + MachineInstr *StoreInsn = nullptr; + bool Modified = false; + + for (; I != E; ++I) { + MachineBasicBlock &MBB = *I; + + MachineBasicBlock::instr_iterator MII = MBB.instr_begin(), + MIE = MBB.instr_end(); + MachineBasicBlock::instr_iterator NextMII; + + // Iterate through the instructions in the basic block + for (; MII != MIE; MII = NextMII) { + NextMII = std::next(MII); + MachineInstr *MI = &*MII; + unsigned Opcode = MI->getOpcode(); + + // Don't process bundled instructions or pseudo operations + if (MI->isBundle() || MI->isTransient()) + continue; + + if (MI->isCall() || MI->isBranch() || MI->isReturn()) { + if (StoreInsn) { + if (!(MI->isBranch() && (MI->getOpcode() != Xtensa::J) && + (MI->getOpcode() != Xtensa::JX))) { + DebugLoc dl = MI->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::MEMW); + MachineBasicBlock::instr_iterator BranchI = MI->getIterator(); + while (((*BranchI).isBranch() || (*BranchI).isCall() || + (*BranchI).isReturn()) && + (BranchI != MBB.instr_begin())) + BranchI = std::prev(BranchI); + + if (BranchI != MBB.instr_begin()) + BranchI = std::next(BranchI); + + BuildMI(MBB, BranchI, dl, NewMCID); + Modified = true; + StoreInsn = nullptr; + NumAdded++; + } + } + if (LastHIQIStore) { + DebugLoc dl = LastHIQIStore->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::MEMW); + BuildMI(*LastHIQIStore->getParent(), + std::next(LastHIQIStore->getIterator()), dl, NewMCID); + LastHIQIStore = nullptr; + Modified = true; + NumAdded++; + } + continue; + } + + switch (Opcode) { + case Xtensa::LSI: + case Xtensa::L32I_N: + case Xtensa::L32I: + case Xtensa::L16SI: + case Xtensa::L16UI: + case Xtensa::L8UI: + if (StoreInsn) { + if (!MII->memoperands_empty()) { + MachineMemOperand *MMO = *MII->memoperands_begin(); + if (!MMO->isVolatile()) { + DebugLoc dl = MII->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::MEMW); + BuildMI(MBB, MII, dl, NewMCID); + Modified = true; + StoreInsn = nullptr; + NumAdded++; + } + } + } + if (LastHIQIStore) { + DebugLoc dl = LastHIQIStore->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::MEMW); + BuildMI(*LastHIQIStore->getParent(), + std::next(LastHIQIStore->getIterator()), dl, NewMCID); + LastHIQIStore = nullptr; + Modified = true; + NumAdded++; + } + break; + case Xtensa::SSI: + case Xtensa::S32I_N: + case Xtensa::S32I: { + LastHIQIStore = nullptr; + StoreInsn = MI; + } break; + case Xtensa::S16I: + case Xtensa::S8I: { + if (!MII->memoperands_empty()) { + MachineMemOperand *MMO = *MII->memoperands_begin(); + if (!MMO->isVolatile()) { + LastHIQIStore = MI; + } + } + StoreInsn = MI; + } break; + } + } + } + return Modified; +} + +bool createXtensaPSRAMCacheFix::xtensaInsertMemwReorg(MachineFunction &MF) { + MachineFunction::iterator I = MF.begin(), E = MF.end(); + bool Modified = false; + bool HadMemw = false; + + for (; I != E; ++I) { + MachineBasicBlock &MBB = *I; + + MachineBasicBlock::instr_iterator MII = MBB.instr_begin(), + MIE = MBB.instr_end(); + MachineBasicBlock::instr_iterator NextMII; + + // Iterate through the instructions in the basic block + for (; MII != MIE; MII = NextMII) { + NextMII = std::next(MII); + MachineInstr *MI = &*MII; + unsigned Opcode = MI->getOpcode(); + + // Don't process bundled instructions or pseudo operations + if (MI->isBundle() || MI->isTransient()) + continue; + + switch (Opcode) { + case Xtensa::LSI: + case Xtensa::L32I_N: + case Xtensa::L32I: + case Xtensa::L16SI: + case Xtensa::L16UI: + case Xtensa::L8UI: { + if (!MII->memoperands_empty()) { + MachineMemOperand *MMO = *MII->memoperands_begin(); + if (!MMO->isVolatile() && (!HadMemw)) { + DebugLoc dl = MII->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::MEMW); + BuildMI(MBB, MII, dl, NewMCID); + Modified = true; + NumAdded++; + } + } + HadMemw = false; + } break; + case Xtensa::SSI: + case Xtensa::S32I_N: + case Xtensa::S32I: + case Xtensa::S16I: + case Xtensa::S8I: { + if (!MII->memoperands_empty()) { + MachineMemOperand *MMO = *MII->memoperands_begin(); + if (!MMO->isVolatile()) { + DebugLoc dl = MII->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::MEMW); + BuildMI(MBB, NextMII, dl, NewMCID); + Modified = true; + NumAdded++; + } + } + HadMemw = true; + } break; + default: + HadMemw = false; + break; + } + } + } + return Modified; +} + +bool createXtensaPSRAMCacheFix::runOnMachineFunction(MachineFunction &MF) { + + Subtarget = &static_cast(MF.getSubtarget()); + XtensaII = static_cast(Subtarget->getInstrInfo()); + bool Modified = false; + + if (AlwaysMembarrier) + return xtensaInsertMemwReorg(MF); + + if (!FixESP32PSRAMCacheIssue) + return false; + + if (ESP32PSRAMFixStrat == ESP32_PSRAM_FIX_MEMW) { + Modified = xtensaPSRAMCacheFixMemwReorg(MF); + } else if (ESP32PSRAMFixStrat == ESP32_PSRAM_FIX_NOPS) { + Modified = xtensaPSRAMCacheFixNopReorg(MF); + } + + return Modified; +} + +FunctionPass *llvm::createXtensaPSRAMCacheFixPass() { + return new createXtensaPSRAMCacheFix(); +} + diff --git a/llvm/lib/Target/Xtensa/XtensaFixupHWLoops.cpp b/llvm/lib/Target/Xtensa/XtensaFixupHWLoops.cpp new file mode 100644 index 0000000000000..6250aa938f83c --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaFixupHWLoops.cpp @@ -0,0 +1,460 @@ +//===---- XtensaFixupHWLoops.cpp - Fixup HW loops -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +//===----------------------------------------------------------------------===// + +#include "Xtensa.h" +#include "XtensaTargetMachine.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/Pass.h" +#include "llvm/Support/MathExtras.h" + +using namespace llvm; + +namespace llvm { +FunctionPass *createXtensaFixupHwLoops(); +void initializeXtensaFixupHwLoopsPass(PassRegistry &); +} // namespace llvm + +namespace { +class XtensaFixupHwLoops : public MachineFunctionPass { + // BasicBlockInfo - Information about the offset and size of a single + // basic block. + struct BasicBlockInfo { + // Offset - Distance from the beginning of the function to the beginning + // of this basic block. + // + // The offset is always aligned as required by the basic block. + unsigned Offset = 0; + + // Size - Size of the basic block in bytes. If the block contains + // inline assembly, this is a worst case estimate. + // + // The size does not include any alignment padding whether from the + // beginning of the block, or from an aligned jump table at the end. + unsigned Size = 0; + + BasicBlockInfo() = default; + + // Compute the offset immediately following this block. \p MBB is the next + // block. + unsigned postOffset(const MachineBasicBlock &MBB) const { + const unsigned PO = Offset + Size; + const Align Alignment = MBB.getAlignment(); + if (Alignment == 1) + return PO; + + const Align ParentAlign = MBB.getParent()->getAlignment(); + if (Alignment <= ParentAlign) + return PO + offsetToAlignment(PO, Alignment); + + // The alignment of this MBB is larger than the function's alignment, so + // we can't tell whether or not it will insert nops. Assume that it will. + return PO + Alignment.value() + offsetToAlignment(PO, Alignment); + } + }; + + SmallVector BlockInfo; + SmallPtrSet AnalyzedMBBs; + + MachineFunction *MF; + MachineLoopInfo *MLI; + const TargetRegisterInfo *TRI; + const TargetInstrInfo *TII; + + bool processLoop(MachineLoop *L); + + bool fixupLoopInstrs(MachineLoop *L); + + void scanFunction(); + + uint64_t computeBlockSize(const MachineBasicBlock &MBB) const; + + void adjustBlockOffsets(MachineBasicBlock &Start); + +public: + static char ID; + + XtensaFixupHwLoops() : MachineFunctionPass(ID) { + initializeXtensaFixupHwLoopsPass(*PassRegistry::getPassRegistry()); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + + StringRef getPassName() const override { + return "Xtensa Hardware Loop Fixup"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addRequired(); + MachineFunctionPass::getAnalysisUsage(AU); + } +}; + +char XtensaFixupHwLoops::ID = 0; +} // namespace + +INITIALIZE_PASS(XtensaFixupHwLoops, "hwloopsfixup", + "Xtensa Hardware Loops Fixup", false, false) + +FunctionPass *llvm::createXtensaFixupHwLoops() { + return new XtensaFixupHwLoops(); +} + +// Returns true if the instruction is a hardware loop instruction. +static bool isHardwareLoop(const MachineInstr &MI) { + return (MI.getOpcode() == Xtensa::LOOPSTART); +} + +bool XtensaFixupHwLoops::runOnMachineFunction(MachineFunction &mf) { + if (skipFunction(mf.getFunction())) + return false; + + MF = &mf; + MLI = &getAnalysis(); + const TargetSubtargetInfo &ST = mf.getSubtarget(); + TII = ST.getInstrInfo(); + TRI = ST.getRegisterInfo(); + + // Renumber all of the machine basic blocks in the function, guaranteeing that + // the numbers agree with the position of the block in the function. + mf.RenumberBlocks(); + + // Do the initial scan of the function, building up information about the + // sizes of each block. + scanFunction(); + + AnalyzedMBBs.clear(); + + bool Changed = false; + + for (auto &L : *MLI) + if (!L->getParentLoop()) { + Changed |= processLoop(L); + } + + return Changed; +} + +// Scan loop and find hardware loop pseudo instructions LOOPSTART and LOOPEND. +// Transform LOOPSTART to Xtensa instructions and remove LOOPEND. +bool XtensaFixupHwLoops::fixupLoopInstrs(MachineLoop *L) { + MachineBasicBlock &MBB = *(L->getHeader()); + const TargetInstrInfo *TII = MBB.getParent()->getSubtarget().getInstrInfo(); + bool Changed = false; + unsigned Num = MBB.getNumber(); + unsigned Offset = BlockInfo[Num].Offset; + MachineBasicBlock *LastBlock = nullptr; + unsigned LHOffset = Offset; + unsigned LastBlockOffset = 0; + + // Loop over all the instructions. + MachineBasicBlock::iterator MII = MBB.begin(); + MachineBasicBlock::iterator MIE = MBB.end(); + MachineInstr *PredI1 = nullptr; + MachineInstr *FirstMI = nullptr; + + // Find appropriate place for the LOOPEND block + for (auto MBI = L->block_begin(), MBIE = L->block_end(); MBI != MBIE; ++MBI) { + MachineBasicBlock *TBB = nullptr, *FBB = nullptr; + SmallVector Cond; + if (!TII->analyzeBranch(*(*MBI), TBB, FBB, Cond)) { + if (FBB && TBB) { + if (LastBlockOffset < BlockInfo[(*MBI)->getNumber()].Offset) { + LastBlockOffset = BlockInfo[(*MBI)->getNumber()].Offset; + LastBlock = (*MBI); + } + } + } + } + + while (MII != MIE) { + if (MII->isMetaInstruction()) { + ++MII; + continue; + } + + MachineInstr &MI = *MII; + + if (FirstMI == nullptr) + FirstMI = &MI; + + if (isHardwareLoop(*MII)) { + MachineBasicBlock *LoopEnd = nullptr; + + MII->getNextNode(); + + MachineBasicBlock::iterator NextMII = std::next(MII); + + // Check whether loop is empty and remove if true + if (NextMII != MIE) { + if ((*NextMII).getOpcode() == Xtensa::LOOPEND) { + MBB.erase(*NextMII); + MBB.erase(*MII); + MBB.removeSuccessor(&MBB, true); + return true; + } + } + + for (MachineBasicBlock::pred_iterator PI = MBB.pred_begin(), + PE = MBB.pred_end(); + PI != PE; ++PI) { + MachineBasicBlock *PMBB = *PI; + MachineBasicBlock::iterator PIB = PMBB->begin(); + MachineBasicBlock::iterator PII = PMBB->end(); + + do { + --PII; + if (PII->isMetaInstruction()) { + continue; + } + + if ((*PII).getOpcode() == Xtensa::LOOPEND) { + DebugLoc DL = PII->getDebugLoc(); + unsigned OffsetLE = BlockInfo[PMBB->getNumber()].Offset; + + // In most cases we expect that blocks in loop are ordered by such manner that block + // with LOOPSTART instruction preceeds block with LOOPEND instruction. + // But in some cases after transformations loop block which contains LOOPEND instruction + // maybe placed before LOOPSTART block during code generaion. We must handle such situation + // because "loop" instruction placed instead of LOOPSTART must have positive offset in the target + // field to the LOOPEND block. + // So, in such situation we add new LOOPEND block after the LOOPSTART block and create jump from old + // LOOPEND block to the new LOOPEND block adn set new LOOPEND block then as target for "loop" instruction + if (OffsetLE < LHOffset) { + LoopEnd = MF->CreateMachineBasicBlock(); + + // If last block in the loop is whithin 256 byte offset from loop instruction + // then just place LOOPEND block after the last block. + if ((LastBlockOffset - LHOffset) < 256) { + //Insert after appropriate block + MF->insert(++LastBlock->getIterator(), LoopEnd); + } else { + // If loop is to large for hardware loop instructuin offset then + // place LoopEnd block just after loop header + MF->insert(++MBB.getIterator(), LoopEnd); + MachineBasicBlock *TBB = nullptr, *FBB = nullptr; + SmallVector Cond; + if (!TII->analyzeBranch(MBB, TBB, FBB, Cond)) { + if (!FBB) { + // LH block just falls through to its succ + for (auto I = MBB.succ_begin(), E = MBB.succ_end(); I != E; + ++I) { + MachineBasicBlock *Succ = *I; + if (Succ != TBB) { + BuildMI(MBB, MBB.end(), DL, TII->get(Xtensa::J)) + .addMBB(Succ); + } + } + } + } + } + + LoopEnd->transferSuccessors(PMBB); + LoopEnd->splice(LoopEnd->end(), PMBB, PII, PMBB->end()); + + MachineBasicBlock::iterator LEI = LoopEnd->end(); + --LEI; + + // Expect jump instruction + assert((LEI->getOpcode() == Xtensa::J) && "Broken hardware loop"); + + // Create block and insert it before loop end address as + // target for jump instruction to avoid premature exit from loop + MachineBasicBlock *BlockForJump = MF->CreateMachineBasicBlock(); + MF->insert(LoopEnd->getIterator(), BlockForJump); + BlockForJump->addSuccessor(LoopEnd); + BuildMI(*BlockForJump, BlockForJump->end(), DL, + TII->get(Xtensa::NOP)); + BuildMI(*PMBB, PMBB->end(), DL, TII->get(Xtensa::J)) + .addMBB(BlockForJump); + PMBB->addSuccessor(BlockForJump); + + BuildMI(*LoopEnd, LoopEnd->begin(), DL, TII->get(Xtensa::LOOPEND)) + .addMBB(LoopEnd); + LoopEnd->addSuccessor(LoopEnd); + Changed = true; + break; + } + if (PII != PIB) { + LoopEnd = MF->CreateMachineBasicBlock(); + MF->insert(++(PMBB->getIterator()), LoopEnd); + LoopEnd->transferSuccessors(PMBB); + LoopEnd->splice(LoopEnd->end(), PMBB, PII, PMBB->end()); + PMBB->addSuccessor(LoopEnd); + + BuildMI(*LoopEnd, LoopEnd->begin(), DL, TII->get(Xtensa::LOOPEND)) + .addMBB(LoopEnd); + LoopEnd->addSuccessor(LoopEnd); + } else { + bool NeedBlockForJump = false; + // Check for branches to the loop end basic block from + // predecessors + for (auto I = PMBB->pred_begin(), E = PMBB->pred_end(); I != E; + ++I) { + MachineBasicBlock *PLEMBB = *I; + MachineBasicBlock *TBB = nullptr, *FBB = nullptr; + SmallVector Cond; + if (!TII->analyzeBranch(*PLEMBB, TBB, FBB, Cond)) { + if (TBB == PMBB) { + NeedBlockForJump = true; + break; + } + } else { + NeedBlockForJump = true; + break; + } + } + // Create block and insert it before loop end address as + // target for jump/branch instruction to avoid premature exit from + // loop + if (NeedBlockForJump) { + LoopEnd = MF->CreateMachineBasicBlock(); + MF->insert(++(PMBB->getIterator()), LoopEnd); + LoopEnd->transferSuccessors(PMBB); + LoopEnd->splice(LoopEnd->end(), PMBB, PII, PMBB->end()); + PMBB->addSuccessor(LoopEnd); + BuildMI(*PMBB, PMBB->end(), DL, TII->get(Xtensa::NOP)); + + BuildMI(*LoopEnd, LoopEnd->begin(), DL, + TII->get(Xtensa::LOOPEND)) + .addMBB(LoopEnd); + LoopEnd->addSuccessor(LoopEnd); + } else { + BuildMI(*PMBB, PII, DL, TII->get(Xtensa::LOOPEND)).addMBB(PMBB); + PMBB->addSuccessor(PMBB); + BuildMI(*PMBB, PII, DL, TII->get(Xtensa::NOP)); + LoopEnd = PMBB; + } + } + + Changed = true; + break; + } + } while (PII != PIB); + if (Changed) + break; + } + + assert((Changed) && "Broken hardware loop"); + + if (MII != FirstMI) { + MBB.splice(FirstMI->getIterator(), &MBB, MII); + Offset = BlockInfo[Num].Offset; + switch (PredI1->getOpcode()) { + case Xtensa::L32I_N: + if (PredI1->getOperand(0).getReg() == MII->getOperand(0).getReg()) { + MBB.splice(MII, &MBB, PredI1); + Offset += 2; + } + break; + case Xtensa::L32I: + if (PredI1->getOperand(0).getReg() == MII->getOperand(0).getReg()) { + MBB.splice(MII, &MBB, PredI1); + Offset += 3; + } + break; + } + } + + DebugLoc DL = MII->getDebugLoc(); + + // Fixup Loop alignment + switch (Offset & 0x3) { + case 0x0: + BuildMI(MBB, MII, DL, TII->get(Xtensa::NOP)); + BuildMI(MBB, MII, DL, TII->get(Xtensa::NOP)); + break; + case 0x3: + BuildMI(MBB, MII, DL, TII->get(Xtensa::NOP)); + break; + } + + BuildMI(MBB, MII, DL, TII->get(Xtensa::LOOP)) + .addReg(MII->getOperand(0).getReg()) + .addMBB(LoopEnd); + MBB.erase(MII); + + MF->RenumberBlocks(); + scanFunction(); + AnalyzedMBBs.insert(&MBB); + return true; + } else { + Offset += TII->getInstSizeInBytes(MI); + PredI1 = &MI; + ++MII; + } + } + + return Changed; +} + +bool XtensaFixupHwLoops::processLoop(MachineLoop *L) { + bool Changed = false; + + // Process nested loops first. + for (MachineLoop::iterator I = L->begin(), E = L->end(); I != E; ++I) { + Changed |= processLoop(*I); + } + + if (Changed) + return true; + + return fixupLoopInstrs(L); +} + +// scanFunction - Do the initial scan of the function, building up +// information about each block. +void XtensaFixupHwLoops::scanFunction() { + BlockInfo.clear(); + BlockInfo.resize(MF->getNumBlockIDs()); + + // First thing, compute the size of all basic blocks, and see if the function + // has any inline assembly in it. If so, we have to be conservative about + // alignment assumptions, as we don't know for sure the size of any + // instructions in the inline assembly. + for (MachineBasicBlock &MBB : *MF) + BlockInfo[MBB.getNumber()].Size = computeBlockSize(MBB); + + // Compute block offsets and known bits. + adjustBlockOffsets(*MF->begin()); +} + +// computeBlockSize - Compute the size for MBB. +uint64_t +XtensaFixupHwLoops::computeBlockSize(const MachineBasicBlock &MBB) const { + uint64_t Size = 0; + for (const MachineInstr &MI : MBB) + if (MI.getOpcode() != Xtensa::LOOPEND) + Size += TII->getInstSizeInBytes(MI); + return Size; +} + +void XtensaFixupHwLoops::adjustBlockOffsets(MachineBasicBlock &Start) { + unsigned PrevNum = Start.getNumber(); + for (auto &MBB : make_range(MachineFunction::iterator(Start), MF->end())) { + unsigned Num = MBB.getNumber(); + if (!Num) // block zero is never changed from offset zero. + continue; + // Get the offset and known bits at the end of the layout predecessor. + // Include the alignment of the current block. + BlockInfo[Num].Offset = BlockInfo[PrevNum].postOffset(MBB); + + PrevNum = Num; + } +} + diff --git a/llvm/lib/Target/Xtensa/XtensaFrameLowering.cpp b/llvm/lib/Target/Xtensa/XtensaFrameLowering.cpp new file mode 100644 index 0000000000000..6b4b43ee38729 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaFrameLowering.cpp @@ -0,0 +1,386 @@ +//===- XtensaFrameLowering.cpp - Xtensa Frame Information -----------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the Xtensa implementation of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#include "XtensaFrameLowering.h" +#include "XtensaInstrInfo.h" +#include "XtensaMachineFunctionInfo.h" +#include "XtensaSubtarget.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/IR/Function.h" + +using namespace llvm; + +XtensaFrameLowering::XtensaFrameLowering() + : TargetFrameLowering(TargetFrameLowering::StackGrowsDown, Align(4), 0, + Align(4)) {} + +/* Xtensa stack frames look like: + + +-------------------------------+ + | incoming stack arguments | + +-------------------------------+ + A | caller-allocated save area | + | for register arguments | + +-------------------------------+ <-- incoming stack pointer + B | CALL0 ABI: | + | callee-allocated save area | + | for arguments that are | + | split between registers and | + | the stack (Register-Spill | + | Area) | + | | + | Win ABI: | + | Register-Spill Overflow | + | 8 words for CALL8/CALLX8 | + +-------------------------------+ <-- arg_pointer_rtx + C | callee-allocated save area | + | for register varargs | + +-------------------------------+ <-- hard_frame_pointer_rtx; + | | stack_pointer_rtx + gp_sp_offset + | GPR save area | + UNITS_PER_WORD + +-------------------------------+ <-- stack_pointer_rtx + fp_sp_offset + | | + UNITS_PER_HWVALUE + | FPR save area | + +-------------------------------+ <-- frame_pointer_rtx (virtual) + | local variables | + P +-------------------------------+ + | outgoing stack arguments | + +-------------------------------+ + | caller-allocated save area | + | for register arguments | + +-------------------------------+ <-- stack_pointer_rtx + + At least two of A, B and C will be empty. + + Dynamic stack allocations such as alloca insert data at point P. + They decrease stack_pointer_rtx but leave frame_pointer_rtx and + hard_frame_pointer_rtx unchanged. */ + +// hasFP - Return true if the specified function should have a dedicated frame +// pointer register. This is true if the function has variable sized allocas or +// if frame pointer elimination is disabled. +bool XtensaFrameLowering::hasFP(const MachineFunction &MF) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + return MF.getTarget().Options.DisableFramePointerElim(MF) || + MFI.hasVarSizedObjects(); +} + +/* minimum frame = reg save area (4 words) plus static chain (1 word) + and the total number of words must be a multiple of 128 bits. */ +/* Width of a word, in units (bytes). */ +#define UNITS_PER_WORD 4 +#define MIN_FRAME_SIZE (8 * UNITS_PER_WORD) + +void XtensaFrameLowering::emitPrologue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + assert(&MBB == &MF.front() && "Shrink-wrapping not yet implemented"); + MachineFrameInfo &MFI = MF.getFrameInfo(); + const XtensaRegisterInfo *RegInfo = static_cast( + MF.getSubtarget().getRegisterInfo()); + const XtensaInstrInfo &TII = + *static_cast(MF.getSubtarget().getInstrInfo()); + MachineBasicBlock::iterator MBBI = MBB.begin(); + const XtensaSubtarget &STI = MF.getSubtarget(); + DebugLoc dl = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); + unsigned SP = Xtensa::SP; + unsigned FP = RegInfo->getFrameRegister(MF); + MachineModuleInfo &MMI = MF.getMMI(); + const MCRegisterInfo *MRI = MMI.getContext().getRegisterInfo(); + XtensaFunctionInfo *XtensaFI = MF.getInfo(); + + // First, compute final stack size. + uint64_t StackSize = MFI.getStackSize(); + uint64_t PrevStackSize = StackSize; + + // Round up StackSize to 16*N + StackSize += (16 - StackSize) & 0xf; + + if (STI.isWinABI()) { + StackSize += 32; + + if (StackSize <= 32760) { + BuildMI(MBB, MBBI, dl, TII.get(Xtensa::ENTRY)) + .addReg(SP) + .addImm(StackSize); + } else { + /* Use a8 as a temporary since a0-a7 may be live. */ + unsigned TmpReg = Xtensa::A8; + + const XtensaInstrInfo &TII = *static_cast( + MBB.getParent()->getSubtarget().getInstrInfo()); + BuildMI(MBB, MBBI, dl, TII.get(Xtensa::ENTRY)) + .addReg(SP) + .addImm(MIN_FRAME_SIZE); + TII.loadImmediate(MBB, MBBI, &TmpReg, StackSize - MIN_FRAME_SIZE); + BuildMI(MBB, MBBI, dl, TII.get(Xtensa::SUB), TmpReg) + .addReg(SP) + .addReg(TmpReg); + BuildMI(MBB, MBBI, dl, TII.get(Xtensa::MOVSP), SP).addReg(TmpReg); + } + + // Store FP register in A8, because FP may be used to pass function + // arguments + if (XtensaFI->isSaveFrameRegister()) { + BuildMI(MBB, MBBI, dl, TII.get(Xtensa::OR), Xtensa::A8) + .addReg(FP) + .addReg(FP); + } + + // if framepointer enabled, set it to point to the stack pointer. + if (hasFP(MF)) { + // Insert instruction "move $fp, $sp" at this location. + BuildMI(MBB, MBBI, dl, TII.get(Xtensa::OR), FP) + .addReg(SP) + .addReg(SP) + .setMIFlag(MachineInstr::FrameSetup); + + MCCFIInstruction Inst = MCCFIInstruction::cfiDefCfa( + nullptr, MRI->getDwarfRegNum(FP, true), StackSize); + unsigned CFIIndex = MF.addFrameInst(Inst); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + } else { + // emit ".cfi_def_cfa_offset StackSize" + unsigned CFIIndex = MF.addFrameInst( + MCCFIInstruction::cfiDefCfaOffset(nullptr, StackSize)); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + } + } else { + // No need to allocate space on the stack. + if (StackSize == 0 && !MFI.adjustsStack()) + return; + + // Adjust stack. + TII.adjustStackPtr(SP, -StackSize, MBB, MBBI); + + // emit ".cfi_def_cfa_offset StackSize" + unsigned CFIIndex = MF.addFrameInst( + MCCFIInstruction::cfiDefCfaOffset(nullptr, StackSize)); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + + const std::vector &CSI = MFI.getCalleeSavedInfo(); + + if (CSI.size()) { + // Find the instruction past the last instruction that saves a + // callee-saved register to the stack. + for (unsigned i = 0; i < CSI.size(); ++i) + ++MBBI; + + // Iterate over list of callee-saved registers and emit .cfi_offset + // directives. + for (const auto &I : CSI) { + int64_t Offset = MFI.getObjectOffset(I.getFrameIdx()); + unsigned Reg = I.getReg(); + + unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset( + nullptr, MRI->getDwarfRegNum(Reg, 1), Offset)); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + } + } + + // if framepointer enabled, set it to point to the stack pointer. + if (hasFP(MF)) { + // Insert instruction "move $fp, $sp" at this location. + BuildMI(MBB, MBBI, dl, TII.get(Xtensa::OR), FP) + .addReg(SP) + .addReg(SP) + .setMIFlag(MachineInstr::FrameSetup); + + // emit ".cfi_def_cfa_register $fp" + unsigned CFIIndex = + MF.addFrameInst(MCCFIInstruction::createDefCfaRegister( + nullptr, MRI->getDwarfRegNum(FP, true))); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + } + } + + if (StackSize != PrevStackSize) { + MFI.setStackSize(StackSize); + + for (int i = MFI.getObjectIndexBegin(); i < MFI.getObjectIndexEnd(); i++) { + if (!MFI.isDeadObjectIndex(i)) { + int64_t SPOffset = MFI.getObjectOffset(i); + + if (SPOffset < 0) + MFI.setObjectOffset(i, SPOffset - StackSize + PrevStackSize); + } + } + } +} + +void XtensaFrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + const XtensaRegisterInfo *RegInfo = static_cast( + MF.getSubtarget().getRegisterInfo()); + const XtensaInstrInfo &TII = + *static_cast(MF.getSubtarget().getInstrInfo()); + const XtensaSubtarget &STI = MF.getSubtarget(); + DebugLoc dl = MBBI->getDebugLoc(); + unsigned SP = Xtensa::SP; + unsigned FP = RegInfo->getFrameRegister(MF); + + // if framepointer enabled, restore the stack pointer. + if (hasFP(MF)) { + // Find the first instruction that restores a callee-saved register. + MachineBasicBlock::iterator I = MBBI; + + for (unsigned i = 0; i < MFI.getCalleeSavedInfo().size(); ++i) + --I; + if (STI.isWinABI()) { + // In most architectures, we need to explicitly restore the stack pointer + // before returning. + // + // For Xtensa Windowed Register option, it is not needed to explicitly + // restore the stack pointer. Reason being is that on function return, + // the window of the caller (including the old stack pointer) gets + // restored anyways. + } else { + BuildMI(MBB, I, dl, TII.get(Xtensa::OR), SP).addReg(FP).addReg(FP); + } + } + + if (STI.isWinABI()) + return; + + // Get the number of bytes from FrameInfo + uint64_t StackSize = MFI.getStackSize(); + + if (!StackSize) + return; + + // Adjust stack. + TII.adjustStackPtr(SP, StackSize, MBB, MBBI); +} + +bool XtensaFrameLowering::spillCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + ArrayRef CSI, const TargetRegisterInfo *TRI) const { + MachineFunction *MF = MBB.getParent(); + const XtensaSubtarget &STI = MF->getSubtarget(); + + if (STI.isWinABI()) + return true; + + MachineBasicBlock &EntryBlock = *(MF->begin()); + const TargetInstrInfo &TII = *MF->getSubtarget().getInstrInfo(); + + for (unsigned i = 0, e = CSI.size(); i != e; ++i) { + // Add the callee-saved register as live-in. Do not add if the register is + // A0 and return address is taken, because it will be implemented in + // method XtensaTargetLowering::LowerRETURNADDR. + // It's killed at the spill, unless the register is RA and return address + // is taken. + unsigned Reg = CSI[i].getReg(); + bool IsA0AndRetAddrIsTaken = + (Reg == Xtensa::A0) && MF->getFrameInfo().isReturnAddressTaken(); + if (!IsA0AndRetAddrIsTaken) + EntryBlock.addLiveIn(Reg); + + // Insert the spill to the stack frame. + bool IsKill = !IsA0AndRetAddrIsTaken; + const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg); + TII.storeRegToStackSlot(EntryBlock, MI, Reg, IsKill, CSI[i].getFrameIdx(), + RC, TRI); + } + + return true; +} + +bool XtensaFrameLowering::restoreCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + MutableArrayRef CSI, const TargetRegisterInfo *TRI) const { + MachineFunction *MF = MBB.getParent(); + const XtensaSubtarget &STI = MF->getSubtarget(); + if (STI.isWinABI()) + return true; + return TargetFrameLowering::restoreCalleeSavedRegisters(MBB, MI, CSI, TRI); +} + +// Eliminate ADJCALLSTACKDOWN, ADJCALLSTACKUP pseudo instructions +MachineBasicBlock::iterator XtensaFrameLowering::eliminateCallFramePseudoInstr( + MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + const XtensaInstrInfo &TII = + *static_cast(MF.getSubtarget().getInstrInfo()); + + if (!hasReservedCallFrame(MF)) { + int64_t Amount = I->getOperand(0).getImm(); + + if (I->getOpcode() == Xtensa::ADJCALLSTACKDOWN) + Amount = -Amount; + + unsigned SP = Xtensa::SP; + TII.adjustStackPtr(SP, Amount, MBB, I); + } + + return MBB.erase(I); +} + +void XtensaFrameLowering::determineCalleeSaves(MachineFunction &MF, + BitVector &SavedRegs, + RegScavenger *RS) const { + const XtensaSubtarget &STI = MF.getSubtarget(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + const XtensaRegisterInfo *RegInfo = static_cast( + MF.getSubtarget().getRegisterInfo()); + unsigned FP = RegInfo->getFrameRegister(MF); + + if (STI.isWinABI()) { + return; + } + + TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS); + + // Mark $fp as used if function has dedicated frame pointer. + if (hasFP(MF)) + SavedRegs.set(FP); + + // Set scavenging frame index if necessary. + uint64_t MaxSPOffset = MFI.estimateStackSize(MF); + + if (isInt<12>(MaxSPOffset)) + return; + + const TargetRegisterClass &RC = Xtensa::ARRegClass; + const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); + unsigned Size = TRI->getSpillSize(RC); + Align Alignment = TRI->getSpillAlign(RC); + int FI = MF.getFrameInfo().CreateStackObject(Size, Alignment, false); + RS->addScavengingFrameIndex(FI); +} + +void XtensaFrameLowering::processFunctionBeforeFrameFinalized( + MachineFunction &MF, RegScavenger *RS) const { + const XtensaSubtarget &STI = MF.getSubtarget(); + + // In WinABI mode add register scavenging slot + // FIXME: It may be posssible to add spill slot by more optimal way + if (STI.isWinABI() && (MF.getFrameInfo().estimateStackSize(MF) > 256)) { + MachineFrameInfo &MFI = MF.getFrameInfo(); + const TargetRegisterClass &RC = Xtensa::ARRegClass; + const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo(); + unsigned Size = TRI.getSpillSize(RC); + Align Alignment = TRI.getSpillAlign(RC); + RS->addScavengingFrameIndex(MFI.CreateStackObject(Size, Alignment, false)); + } +} diff --git a/llvm/lib/Target/Xtensa/XtensaFrameLowering.h b/llvm/lib/Target/Xtensa/XtensaFrameLowering.h new file mode 100644 index 0000000000000..8eb44c4932660 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaFrameLowering.h @@ -0,0 +1,54 @@ +//===- XtensaFrameLowering.h - Define frame lowering for Xtensa --*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===-----------------------------------------------------------------------==// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSAFRAMELOWERING_H +#define LLVM_LIB_TARGET_XTENSA_XTENSAFRAMELOWERING_H + +#include "llvm/CodeGen/TargetFrameLowering.h" + +namespace llvm { +class XtensaTargetMachine; +class XtensaSubtarget; + +class XtensaFrameLowering : public TargetFrameLowering { +public: + XtensaFrameLowering(); + + bool hasFP(const MachineFunction &MF) const override; + + /// emitProlog/emitEpilog - These methods insert prolog and epilog code into + /// the function. + void emitPrologue(MachineFunction &, MachineBasicBlock &) const override; + void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + MachineBasicBlock::iterator + eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const override; + + bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + ArrayRef CSI, + const TargetRegisterInfo *TRI) const override; + bool + restoreCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + MutableArrayRef CSI, + const TargetRegisterInfo *TRI) const override; + + void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, + RegScavenger *RS) const override; + + void processFunctionBeforeFrameFinalized(MachineFunction &MF, + RegScavenger *RS) const override; +}; + +} // namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_XTENSAFRAMELOWERING_H */ diff --git a/llvm/lib/Target/Xtensa/XtensaHardwareLoops.cpp b/llvm/lib/Target/Xtensa/XtensaHardwareLoops.cpp new file mode 100644 index 0000000000000..f77a98486ec3f --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaHardwareLoops.cpp @@ -0,0 +1,444 @@ +//===- XtensaHardwareLoops.cpp - Idenify and generate hardware Loops ------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains implementation of the pass which optimizes loops . +// +//===----------------------------------------------------------------------===// + +#include "XtensaInstrInfo.h" +#include "XtensaSubtarget.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineLoopInfo.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "xtensa-hwloops" +#define MAX_LOOP_SIZE 256 + +namespace llvm { + +FunctionPass *createXtensaHardwareLoops(); +void initializeXtensaHardwareLoopsPass(PassRegistry &); + +} // end namespace llvm + +namespace { + +struct XtensaHardwareLoops : public MachineFunctionPass { + MachineLoopInfo *MLI; + MachineRegisterInfo *MRI; + MachineDominatorTree *MDT; + const XtensaInstrInfo *TII; + const TargetRegisterInfo *TRI; + const XtensaSubtarget *STI; + SmallPtrSet VisitedMBBs; + +public: + static char ID; + + XtensaHardwareLoops() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &MF) override; + + StringRef getPassName() const override { return "Xtensa Hardware Loops"; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + MachineFunctionPass::getAnalysisUsage(AU); + } + +private: + // Return true if the instruction is not valid within a hardware + // loop. + bool isInvalidLoopOperation(const MachineInstr *MI) const; + + // Return true if the loop contains an instruction that inhibits + // using the hardware loop. + bool containsInvalidInstruction(MachineLoop *L) const; + + // Given a loop, check if we can convert it to a hardware loop. + // If so, then perform the conversion and return true. + bool processLoop(MachineLoop *L); + + bool checkLoopSize(MachineLoop *L); + + bool checkLoopEndDisplacement(MachineFunction &MF, MachineBasicBlock *LH, + MachineBasicBlock *LE); + + void revertNonLoops(MachineFunction &M); +}; + +char XtensaHardwareLoops::ID = 0; + +} // end anonymous namespace + +INITIALIZE_PASS(XtensaHardwareLoops, "hwloops", "Xtensa Hardware Loops", false, + false) + +FunctionPass *llvm::createXtensaHardwareLoops() { + return new XtensaHardwareLoops(); +} + +bool XtensaHardwareLoops::runOnMachineFunction(MachineFunction &MF) { + LLVM_DEBUG(dbgs() << "********* Xtensa Hardware Loops *********\n"); + if (skipFunction(MF.getFunction())) + return false; + + bool Changed = false; + MLI = &getAnalysis(); + MRI = &MF.getRegInfo(); + STI = &MF.getSubtarget(); + TII = STI->getInstrInfo(); + TRI = STI->getRegisterInfo(); + + if (!STI->hasLoop()) + return false; + + VisitedMBBs.clear(); + + for (auto &L : *MLI) + if (!L->getParentLoop()) { + Changed |= processLoop(L); + } + + revertNonLoops(MF); + + return Changed; +} + +// Return true if the operation is invalid within hardware loop. +bool XtensaHardwareLoops::isInvalidLoopOperation(const MachineInstr *MI) const { + + // Call is not allowed because the callee may use a hardware loop + if (MI->getDesc().isCall()) + return true; + + if ((MI->getOpcode() == Xtensa::LOOP) || + (MI->getOpcode() == Xtensa::LOOPGTZ) || + (MI->getOpcode() == Xtensa::LOOPNEZ)) + return true; + + if (MI->isInlineAsm()) + return true; + + return false; +} + +// Return true if the loop contains an instruction that inhibits +// the use of the hardware loop instruction. +bool XtensaHardwareLoops::containsInvalidInstruction(MachineLoop *L) const { + LLVM_DEBUG(dbgs() << "\nhw_loop head, " + << printMBBReference(**L->block_begin())); + for (MachineBasicBlock *MBB : L->getBlocks()) { + for (MachineBasicBlock::iterator MII = MBB->begin(), E = MBB->end(); + MII != E; ++MII) { + const MachineInstr *MI = &*MII; + if (isInvalidLoopOperation(MI)) { + LLVM_DEBUG(dbgs() << "\nCannot convert to hw_loop due to:"; + MI->dump();); + return true; + } + } + } + return false; +} + +// Check if this loop is suitable for converting to a hardware loop +bool XtensaHardwareLoops::processLoop(MachineLoop *L) { + // This is just for sanity. + assert(L->getHeader() && "Loop without a header?"); + + bool Changed = false; + + // Process nested loops first. + for (MachineLoop::iterator I = L->begin(), E = L->end(); I != E; ++I) { + Changed |= processLoop(*I); + } + + if (Changed) + return true; + + using instr_iterator = MachineBasicBlock::instr_iterator; + MachineInstr *LII = nullptr; // LOOPINIT instruction + MachineInstr *LDECI = nullptr; // LOOPDEC instruction + MachineInstr *LBRI = nullptr; // LOOPBR instruction + MachineBasicBlock *LDECMBB = nullptr; + MachineBasicBlock *LBRMBB = nullptr; + MachineBasicBlock *LH = L->getHeader(); + MachineBasicBlock *LastMBB = L->getLoopLatch(); + std::map LoopInitMap; + + // Try to find LOOPENDDEC instruction in the loop latch + for (auto MBI = L->block_begin(), MBIE = L->block_end(); MBI != MBIE; ++MBI) { + if (VisitedMBBs.count(*MBI)) + continue; + for (auto MII = (*MBI)->begin(), MIE = (*MBI)->end(); MII != MIE; ++MII) { + MachineInstr *LMI = &*MII; + if (LMI->getOpcode() == Xtensa::LOOPDEC) { + LDECI = LMI; + LDECMBB = *MBI; + } + + if (LMI->getOpcode() == Xtensa::LOOPBR) { + LBRI = LMI; + LBRMBB = *MBI; + } + + // Collect LOOPINIT instructions inside the loop + if (LMI->getOpcode() == Xtensa::LOOPINIT) { + MachineBasicBlock *SB = LMI->getParent(); + while (!SB->isSuccessor(LH)) { + for (auto SBI : SB->successors()) { + if (!L->contains(SBI)) + continue; + SB = SBI; + break; + } + if (!L->contains(SB)) + llvm_unreachable("Wrong hardware loop"); + } + LoopInitMap[SB] = LMI; + } + } + VisitedMBBs.insert(*MBI); + } + + if ((LBRI != nullptr) && (LDECI != nullptr)) { + MachineBasicBlock *LIMBB = nullptr; + + for (const auto &Use : MRI->use_operands(LDECI->getOperand(0).getReg())) { + const MachineInstr *UseMI = Use.getParent(); + if ((UseMI != LBRI) && (UseMI->getOpcode() != TargetOpcode::PHI)) { + LLVM_DEBUG(dbgs() << "Xtensa Loops: Unable to remove LoopDec.\n"); + return false; + } + } + + // Collect LOOPINIT instructions in predecessors from outter loop + for (auto PBI : LH->predecessors()) { + if (L->contains(PBI)) + continue; + LIMBB = PBI; + LII = nullptr; + // Try to find LOOPINIT instructions in predecessor + while ((LII == nullptr) && (LIMBB != nullptr) && + ((L->getParentLoop() == nullptr) || + (L->getParentLoop()->contains(LIMBB)))) { + for (instr_iterator I = LIMBB->instr_begin(), E = LIMBB->instr_end(); + I != E; ++I) { + MachineInstr *MI = &*I; + if (MI->getOpcode() == Xtensa::LOOPINIT) { + LII = MI; + break; + } + } + if (LII == nullptr) + LIMBB = *LIMBB->pred_begin(); + } + if (LII == nullptr) { + llvm_unreachable("Hardware loop init instruction not found"); + return false; + } + LoopInitMap[PBI] = LII; + } + + DebugLoc DL = LII->getDebugLoc(); + + // If loop is too large or have wrong configuration + // then restore branch instruction + // sub a, a, 1 + // bnez a, LH + if (!checkLoopSize(L) || containsInvalidInstruction(L) || + (LBRMBB != LastMBB) || + (!checkLoopEndDisplacement(*LH->getParent(), LH, LBRMBB))) { + + for (auto PB : LH->predecessors()) { + if (LoopInitMap.find(PB) != LoopInitMap.end()) { + Register Elts = LoopInitMap[PB]->getOperand(1).getReg(); + Register Def = LoopInitMap[PB]->getOperand(0).getReg(); + + for (auto &Use : make_early_inc_range(MRI->use_operands(Def))) { + Use.setReg(Elts); + } + LoopInitMap[PB]->getParent()->erase(LoopInitMap[PB]); + } + } + + Register IndR = LDECI->getOperand(0).getReg(); + Register PR = LDECI->getOperand(1).getReg(); + + BuildMI(*LDECMBB, LDECI, LDECI->getDebugLoc(), TII->get(Xtensa::ADDI), + IndR) + .addReg(PR) + .addImm(-1); + BuildMI(*LBRMBB, LBRI, LBRI->getDebugLoc(), TII->get(Xtensa::BNEZ)) + .addReg(IndR) + .addMBB(LBRI->getOperand(1).getMBB()); + LDECMBB->erase(LDECI); + LBRMBB->erase(LBRI); + return false; + } + + MachineInstr *PN = nullptr; + + for (auto &Use : MRI->use_operands(LDECI->getOperand(0).getReg())) { + MachineInstr *UseMI = Use.getParent(); + if (UseMI->getOpcode() == TargetOpcode::PHI) { + PN = UseMI; + } + } + + assert(((PN != nullptr) && (PN->getParent() == LH)) && + "Expected PHI node successor of the LOOPEND instruction in loop " + "header"); + LII = PN; + + Register EltsDec = LDECI->getOperand(0).getReg(); + Register Elts = LDECI->getOperand(1).getReg(); + + for (MachineOperand &MO : PN->operands()) { + if (!MO.isReg() || MO.getReg() != EltsDec) + continue; + MO.substVirtReg(Elts, 0, *TRI); + } + LDECMBB->erase(LDECI); + + MachineBasicBlock::iterator LHI = LH->getFirstNonPHI(); + + BuildMI(*LH, LHI, DL, TII->get(Xtensa::LOOPSTART)) + .addReg(LII->getOperand(0).getReg()) + .addMBB(LBRMBB); + + if (LII->getOpcode() == Xtensa::LOOPINIT) + LII->getParent()->erase(LII); + + BuildMI(*LBRMBB, LBRI, DL, TII->get(Xtensa::LOOPEND)).addMBB(LH); + LBRMBB->erase(LBRI); + + return true; + } + + return false; +} + +bool XtensaHardwareLoops::checkLoopSize(MachineLoop *L) { + uint64_t LoopSize = 3; //Reserve space for possible NOP + + for (auto *MBB : L->getBlocks()) { + uint64_t BlockSize = 0; + for (const MachineInstr &MI : *MBB) { + uint64_t InstSize = TII->getInstSizeInBytes(MI); + if (MI.isPHI()) + InstSize = 3; + BlockSize += InstSize; + } + LoopSize += BlockSize; + } + + if (LoopSize > MAX_LOOP_SIZE) + return false; + + return true; +} + +bool XtensaHardwareLoops::checkLoopEndDisplacement(MachineFunction &MF, + MachineBasicBlock *LH, + MachineBasicBlock *LE) { + bool isLHVisited = false; + + if (LH == LE) + return true; + + for (MachineFunction::iterator I = MF.begin(), E = MF.end(); I != E; ++I) { + MachineBasicBlock *MBB = &*I; + if (MBB == LH) + isLHVisited = true; + else if (MBB == LE) { + if (isLHVisited) + return true; + else + return false; + } + } + llvm_unreachable("Wrong hardware loop"); +} + +void XtensaHardwareLoops::revertNonLoops(MachineFunction &MF) { + for (MachineFunction::iterator I = MF.begin(); I != MF.end(); ++I) { + MachineBasicBlock &MBB = *I; + + for (MachineBasicBlock::iterator MII = MBB.begin(), E = MBB.end(); MII != E; + ++MII) { + MachineInstr *MI = &*MII; + if (MI->getOpcode() == Xtensa::LOOPINIT) { + MachineInstr *LI = MI; + MachineBasicBlock *LIMBB = LI->getParent(); + Register Elts = LI->getOperand(1).getReg(); + Register Def = LI->getOperand(0).getReg(); + for (auto &Use : make_early_inc_range(MRI->use_operands(Def))) { + Use.setReg(Elts); + } + --MII; + LIMBB->erase(LI); + } else if (MI->getOpcode() == Xtensa::LOOPDEC) { + MachineInstr *LEI = MI; + MachineBasicBlock *LEMBB = LEI->getParent(); + Register IndR = LEI->getOperand(0).getReg(); + Register PR = LEI->getOperand(1).getReg(); + + BuildMI(*LEMBB, LEI, LEI->getDebugLoc(), TII->get(Xtensa::ADDI), IndR) + .addReg(PR) + .addImm(-1); + --MII; + LEMBB->erase(LEI); + } else if (MI->getOpcode() == Xtensa::LOOPBR) { + MachineInstr *LBRI = MI; + MachineBasicBlock *LBRMBB = LBRI->getParent(); + + BuildMI(*LBRMBB, LBRI, LBRI->getDebugLoc(), TII->get(Xtensa::BNEZ)) + .addReg(LBRI->getOperand(0).getReg()) + .addMBB(LBRI->getOperand(1).getMBB()); + --MII; + LBRMBB->erase(LBRI); + } + } + } +} diff --git a/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp b/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp new file mode 100644 index 0000000000000..be95e9a2720a6 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp @@ -0,0 +1,409 @@ +//===- XtensaISelDAGToDAG.cpp - A dag to dag inst selector for Xtensa -----===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines an instruction selector for the Xtensa target. +// +//===----------------------------------------------------------------------===// + +#include "Xtensa.h" +#include "XtensaTargetMachine.h" +#include "llvm/IR/IntrinsicsXtensa.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAGISel.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "xtensa-isel" + +namespace { + +class XtensaDAGToDAGISel : public SelectionDAGISel { + const XtensaSubtarget *Subtarget; + +public: + XtensaDAGToDAGISel(XtensaTargetMachine &TM, CodeGenOpt::Level OptLevel) + : SelectionDAGISel(TM, OptLevel), Subtarget(TM.getSubtargetImpl()) {} + + // Override MachineFunctionPass. + StringRef getPassName() const override { + return "Xtensa DAG->DAG Pattern Instruction Selection"; + } + + // Override SelectionDAGISel. + void Select(SDNode *Node) override; + + bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, + std::vector &OutOps) override; + + bool selectMemRegAddr(SDValue Addr, SDValue &Base, SDValue &Offset, + int Scale) { + EVT ValTy = Addr.getValueType(); + + // if Address is FI, get the TargetFrameIndex. + if (FrameIndexSDNode *FIN = dyn_cast(Addr)) { + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy); + Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), ValTy); + + return true; + } + + if (TM.isPositionIndependent()) + report_fatal_error("PIC relocations is not supported"); + + if ((Addr.getOpcode() == ISD::TargetExternalSymbol || + Addr.getOpcode() == ISD::TargetGlobalAddress)) + return false; + + // Addresses of the form FI+const or FI|const + bool Valid = false; + if (CurDAG->isBaseWithConstantOffset(Addr)) { + ConstantSDNode *CN = dyn_cast(Addr.getOperand(1)); + int64_t OffsetVal = CN->getSExtValue(); + + switch (Scale) { + case 1: + Valid = (OffsetVal >= 0 && OffsetVal <= 255); + break; + case 2: + Valid = + (OffsetVal >= 0 && OffsetVal <= 510) && ((OffsetVal & 0x1) == 0); + break; + case 4: + Valid = + (OffsetVal >= 0 && OffsetVal <= 1020) && ((OffsetVal & 0x3) == 0); + break; + default: + break; + } + + if (Valid) { + // If the first operand is a FI, get the TargetFI Node + if (FrameIndexSDNode *FIN = + dyn_cast(Addr.getOperand(0))) + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy); + else + Base = Addr.getOperand(0); + + Offset = + CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Addr), ValTy); + return true; + } + } + + // Last case + Base = Addr; + Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), Addr.getValueType()); + return true; + } + + bool selectMemRegAddrISH1(SDValue Addr, SDValue &Base, SDValue &Offset) { + return selectMemRegAddr(Addr, Base, Offset, 1); + } + + bool selectMemRegAddrISH2(SDValue Addr, SDValue &Base, SDValue &Offset) { + return selectMemRegAddr(Addr, Base, Offset, 2); + } + + bool selectMemRegAddrISH4(SDValue Addr, SDValue &Base, SDValue &Offset) { + return selectMemRegAddr(Addr, Base, Offset, 4); + } + +// Include the pieces autogenerated from the target description. +#include "XtensaGenDAGISel.inc" +}; // namespace +} // end anonymous namespace + +FunctionPass *llvm::createXtensaISelDag(XtensaTargetMachine &TM, + CodeGenOpt::Level OptLevel) { + return new XtensaDAGToDAGISel(TM, OptLevel); +} + +void XtensaDAGToDAGISel::Select(SDNode *Node) { + unsigned Opcode = Node->getOpcode(); + SDLoc DL(Node); + const unsigned MRTable[] = {Xtensa::M0, Xtensa::M1, Xtensa::M2, Xtensa::M3}; + + switch (Opcode) { + case ISD::INTRINSIC_VOID: { + unsigned IntNo = cast(Node->getOperand(1))->getZExtValue(); + switch (IntNo) { + default: + break; + case Intrinsic::xtensa_mul_da_ll: + case Intrinsic::xtensa_mul_da_lh: + case Intrinsic::xtensa_mul_da_hl: + case Intrinsic::xtensa_mul_da_hh: + case Intrinsic::xtensa_mula_da_ll: + case Intrinsic::xtensa_mula_da_lh: + case Intrinsic::xtensa_mula_da_hl: + case Intrinsic::xtensa_mula_da_hh: + case Intrinsic::xtensa_muls_da_ll: + case Intrinsic::xtensa_muls_da_lh: + case Intrinsic::xtensa_muls_da_hl: + case Intrinsic::xtensa_muls_da_hh: { + SDValue ChainIn = Node->getOperand(0); + SDValue ValueMX = Node->getOperand(2); + SDValue ValueT = Node->getOperand(3); + unsigned OpCode; + + switch (IntNo) { + case Intrinsic::xtensa_mul_da_ll: + OpCode = Xtensa::MUL_DA_LL; + break; + case Intrinsic::xtensa_mul_da_lh: + OpCode = Xtensa::MUL_DA_LH; + break; + case Intrinsic::xtensa_mul_da_hl: + OpCode = Xtensa::MUL_DA_HL; + break; + case Intrinsic::xtensa_mul_da_hh: + OpCode = Xtensa::MUL_DA_HH; + break; + case Intrinsic::xtensa_mula_da_ll: + OpCode = Xtensa::MULA_DA_LL; + break; + case Intrinsic::xtensa_mula_da_lh: + OpCode = Xtensa::MULA_DA_LH; + break; + case Intrinsic::xtensa_mula_da_hl: + OpCode = Xtensa::MULA_DA_HL; + break; + case Intrinsic::xtensa_mula_da_hh: + OpCode = Xtensa::MULA_DA_HH; + break; + case Intrinsic::xtensa_muls_da_ll: + OpCode = Xtensa::MULS_DA_LL; + break; + case Intrinsic::xtensa_muls_da_lh: + OpCode = Xtensa::MULS_DA_LH; + break; + case Intrinsic::xtensa_muls_da_hl: + OpCode = Xtensa::MULS_DA_HL; + break; + case Intrinsic::xtensa_muls_da_hh: + OpCode = Xtensa::MULS_DA_HH; + break; + } + + uint64_t MXVal = 4; + if (ValueMX.getOpcode() == ISD::TargetConstant) { + MXVal = cast(ValueMX)->getZExtValue(); + } + + assert( + (MXVal < 2) && + "Unexpected value of mul*_da* first argument, it must be m0 or m1"); + unsigned MXReg = MRTable[MXVal]; + + const EVT MULAResTys[] = {MVT::Other}; + SmallVector MULAOps; + MULAOps.push_back(CurDAG->getRegister(MXReg, MVT::i32)); + MULAOps.push_back(ValueT); + MULAOps.push_back(ChainIn); + + SDNode *MULA = CurDAG->getMachineNode(OpCode, DL, MULAResTys, MULAOps); + ReplaceNode(Node, MULA); + return; + } + case Intrinsic::xtensa_mul_ad_ll: + case Intrinsic::xtensa_mul_ad_lh: + case Intrinsic::xtensa_mul_ad_hl: + case Intrinsic::xtensa_mul_ad_hh: + case Intrinsic::xtensa_mula_ad_ll: + case Intrinsic::xtensa_mula_ad_lh: + case Intrinsic::xtensa_mula_ad_hl: + case Intrinsic::xtensa_mula_ad_hh: + case Intrinsic::xtensa_muls_ad_ll: + case Intrinsic::xtensa_muls_ad_lh: + case Intrinsic::xtensa_muls_ad_hl: + case Intrinsic::xtensa_muls_ad_hh: { + SDValue ChainIn = Node->getOperand(0); + SDValue ValueS = Node->getOperand(2); + SDValue ValueMY = Node->getOperand(3); + unsigned OpCode; + + switch (IntNo) { + case Intrinsic::xtensa_mul_ad_ll: + OpCode = Xtensa::MUL_AD_LL; + break; + case Intrinsic::xtensa_mul_ad_lh: + OpCode = Xtensa::MUL_AD_LH; + break; + case Intrinsic::xtensa_mul_ad_hl: + OpCode = Xtensa::MUL_AD_HL; + break; + case Intrinsic::xtensa_mul_ad_hh: + OpCode = Xtensa::MUL_AD_HH; + break; + case Intrinsic::xtensa_mula_ad_ll: + OpCode = Xtensa::MULA_AD_LL; + break; + case Intrinsic::xtensa_mula_ad_lh: + OpCode = Xtensa::MULA_AD_LH; + break; + case Intrinsic::xtensa_mula_ad_hl: + OpCode = Xtensa::MULA_AD_HL; + break; + case Intrinsic::xtensa_mula_ad_hh: + OpCode = Xtensa::MULA_AD_HH; + break; + case Intrinsic::xtensa_muls_ad_ll: + OpCode = Xtensa::MULS_AD_LL; + break; + case Intrinsic::xtensa_muls_ad_lh: + OpCode = Xtensa::MULS_AD_LH; + break; + case Intrinsic::xtensa_muls_ad_hl: + OpCode = Xtensa::MULS_AD_HL; + break; + case Intrinsic::xtensa_muls_ad_hh: + OpCode = Xtensa::MULS_AD_HH; + break; + } + + uint64_t MYVal = 4; + if (ValueMY.getOpcode() == ISD::TargetConstant) { + MYVal = cast(ValueMY)->getZExtValue(); + } + + assert( + ((MYVal > 1) && (MYVal < 4)) && + "Unexpected value of mul*_ad* second argument, it must be m2 or m3"); + unsigned MYReg = MRTable[MYVal]; + + const EVT MULAResTys[] = {MVT::Other}; + SmallVector MULAOps; + MULAOps.push_back(ValueS); + MULAOps.push_back(CurDAG->getRegister(MYReg, MVT::i32)); + MULAOps.push_back(ChainIn); + + SDNode *MULA = CurDAG->getMachineNode(OpCode, DL, MULAResTys, MULAOps); + ReplaceNode(Node, MULA); + return; + } + case Intrinsic::xtensa_mul_dd_ll: + case Intrinsic::xtensa_mul_dd_lh: + case Intrinsic::xtensa_mul_dd_hl: + case Intrinsic::xtensa_mul_dd_hh: + case Intrinsic::xtensa_mula_dd_ll: + case Intrinsic::xtensa_mula_dd_lh: + case Intrinsic::xtensa_mula_dd_hl: + case Intrinsic::xtensa_mula_dd_hh: + case Intrinsic::xtensa_muls_dd_ll: + case Intrinsic::xtensa_muls_dd_lh: + case Intrinsic::xtensa_muls_dd_hl: + case Intrinsic::xtensa_muls_dd_hh: { + SDValue ChainIn = Node->getOperand(0); + SDValue ValueMX = Node->getOperand(2); + SDValue ValueMY = Node->getOperand(3); + unsigned OpCode; + + switch (IntNo) { + case Intrinsic::xtensa_mul_dd_ll: + OpCode = Xtensa::MUL_DD_LL; + break; + case Intrinsic::xtensa_mul_dd_lh: + OpCode = Xtensa::MUL_DD_LH; + break; + case Intrinsic::xtensa_mul_dd_hl: + OpCode = Xtensa::MUL_DD_HL; + break; + case Intrinsic::xtensa_mul_dd_hh: + OpCode = Xtensa::MUL_DD_HH; + break; + case Intrinsic::xtensa_mula_dd_ll: + OpCode = Xtensa::MULA_DD_LL; + break; + case Intrinsic::xtensa_mula_dd_lh: + OpCode = Xtensa::MULA_DD_LH; + break; + case Intrinsic::xtensa_mula_dd_hl: + OpCode = Xtensa::MULA_DD_HL; + break; + case Intrinsic::xtensa_mula_dd_hh: + OpCode = Xtensa::MULA_DD_HH; + break; + case Intrinsic::xtensa_muls_dd_ll: + OpCode = Xtensa::MULS_DD_LL; + break; + case Intrinsic::xtensa_muls_dd_lh: + OpCode = Xtensa::MULS_DD_LH; + break; + case Intrinsic::xtensa_muls_dd_hl: + OpCode = Xtensa::MULS_DD_HL; + break; + case Intrinsic::xtensa_muls_dd_hh: + OpCode = Xtensa::MULS_DD_HH; + break; + } + uint64_t MXVal = 4; + if (ValueMX.getOpcode() == ISD::TargetConstant) { + MXVal = cast(ValueMX)->getZExtValue(); + } + + assert( + (MXVal < 2) && + "Unexpected value of mul*_dd* first argument, it must be m0 or m1"); + unsigned MXReg = MRTable[MXVal]; + + uint64_t MYVal = 4; + if (ValueMY.getOpcode() == ISD::TargetConstant) { + MYVal = cast(ValueMY)->getZExtValue(); + } + + assert( + ((MYVal > 1) && (MYVal < 4)) && + "Unexpected value of mul*_dd* second argument, it must be m2 or m3"); + unsigned MYReg = MRTable[MYVal]; + + const EVT MULAResTys[] = {MVT::Other}; + SmallVector MULAOps; + MULAOps.push_back(CurDAG->getRegister(MXReg, MVT::i32)); + MULAOps.push_back(CurDAG->getRegister(MYReg, MVT::i32)); + MULAOps.push_back(ChainIn); + + SDNode *MULA = CurDAG->getMachineNode(OpCode, DL, MULAResTys, MULAOps); + ReplaceNode(Node, MULA); + return; + } + } + break; + } + default: + break; + } + + SelectCode(Node); +} + +bool XtensaDAGToDAGISel::SelectInlineAsmMemoryOperand( + const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) { + switch (ConstraintID) { + default: + llvm_unreachable("Unexpected asm memory constraint"); + case InlineAsm::Constraint_m: { + SDValue Base, Offset; + // TODO + selectMemRegAddr(Op, Base, Offset, 4); + OutOps.push_back(Base); + OutOps.push_back(Offset); + return false; + } + case InlineAsm::Constraint_i: + case InlineAsm::Constraint_R: + case InlineAsm::Constraint_ZC: + OutOps.push_back(Op); + return false; + } + return false; +} diff --git a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp new file mode 100644 index 0000000000000..4613728d7de30 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp @@ -0,0 +1,3338 @@ +//===- XtensaISelLowering.cpp - Xtensa DAG Lowering Implementation --------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that Xtensa uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#include "XtensaISelLowering.h" +#include "XtensaConstantPoolValue.h" +#include "XtensaMachineFunctionInfo.h" +#include "XtensaSubtarget.h" +#include "XtensaTargetMachine.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "xtensa-lower" + +static const MCPhysReg XtensaArgRegs[6] = {Xtensa::A2, Xtensa::A3, Xtensa::A4, + Xtensa::A5, Xtensa::A6, Xtensa::A7}; + +// Return true if we must use long (in fact, indirect) function call. +// It's simplified version, production implimentation must +// resolve a functions in ROM (usually glibc functions) +static bool isLongCall(const char *str) { + // Currently always use long calls + return true; +} + +// The calling conventions in XtensaCallingConv.td are described in terms of the +// callee's register window. This function translates registers to the +// corresponding caller window %o register. +static unsigned toCallerWindow(unsigned Reg) { + if (Reg >= Xtensa::A2 && Reg <= Xtensa::A7) + return Reg - Xtensa::A2 + Xtensa::A10; + return Reg; +} + +XtensaTargetLowering::XtensaTargetLowering(const TargetMachine &tm, + const XtensaSubtarget &STI) + : TargetLowering(tm), Subtarget(STI) { + MVT PtrVT = MVT::i32; + // Set up the register classes. + addRegisterClass(MVT::i32, &Xtensa::ARRegClass); + + if (Subtarget.hasSingleFloat()) { + addRegisterClass(MVT::f32, &Xtensa::FPRRegClass); + } + + // Set up special registers. + setStackPointerRegisterToSaveRestore(Xtensa::SP); + + setSchedulingPreference(Sched::RegPressure); + + setBooleanContents(ZeroOrOneBooleanContent); + setBooleanVectorContents(ZeroOrOneBooleanContent); + + setMinFunctionAlignment(Align(4)); + + setOperationAction(ISD::Constant, MVT::i32, Custom); + setOperationAction(ISD::Constant, MVT::i64, Expand); + setOperationAction(ISD::ConstantFP, MVT::f32, Custom); + setOperationAction(ISD::ConstantFP, MVT::f64, Expand); + + // No sign extend instructions for i1 + for (MVT VT : MVT::integer_valuetypes()) { + setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i1, Promote); + setLoadExtAction(ISD::ZEXTLOAD, VT, MVT::i1, Promote); + setLoadExtAction(ISD::EXTLOAD, VT, MVT::i1, Promote); + } + + // Handle the various types of symbolic address. + setOperationAction(ISD::ConstantPool, PtrVT, Custom); + setOperationAction(ISD::GlobalAddress, PtrVT, Custom); + setOperationAction(ISD::GlobalTLSAddress, PtrVT, Custom); + setOperationAction(ISD::BlockAddress, PtrVT, Custom); + setOperationAction(ISD::JumpTable, PtrVT, Custom); + + // Used by legalize types to correctly generate the setcc result. + // AddPromotedToType(ISD::SETCC, MVT::i1, MVT::i32); + setOperationPromotedToType(ISD::SETCC, MVT::i1, MVT::i32); + setOperationPromotedToType(ISD::BR_CC, MVT::i1, MVT::i32); + + setOperationAction(ISD::BR_CC, MVT::i32, Legal); + setOperationAction(ISD::BR_CC, MVT::i64, Expand); + if (Subtarget.hasSingleFloat()) + setOperationAction(ISD::BR_CC, MVT::f32, Custom); + else + setOperationAction(ISD::BR_CC, MVT::f32, Expand); + + setOperationAction(ISD::SELECT, MVT::i32, Expand); + setOperationAction(ISD::SELECT, MVT::i64, Expand); + setOperationAction(ISD::SELECT, MVT::f32, Expand); + + setOperationAction(ISD::SELECT_CC, MVT::i32, Custom); + setOperationAction(ISD::SELECT_CC, MVT::i64, Expand); + if (Subtarget.hasSingleFloat()) + setOperationAction(ISD::SELECT_CC, MVT::f32, Custom); + else + setOperationAction(ISD::SELECT_CC, MVT::f32, Expand); + + setOperationAction(ISD::SETCC, MVT::i32, + Custom); // folds into brcond + setOperationAction(ISD::SETCC, MVT::i64, Expand); + if (Subtarget.hasSingleFloat()) + setOperationAction(ISD::SETCC, MVT::f32, Custom); + else + setOperationAction(ISD::SETCC, MVT::f32, Expand); + + // Expand jump table branches as address arithmetic followed by an + // indirect jump. + setOperationAction(ISD::BR_JT, MVT::Other, Custom); + + // make BRCOND legal, its actually only legal for a subset of conds + setOperationAction(ISD::BRCOND, MVT::Other, Legal); + + // Handle integer types. + for (unsigned I = MVT::FIRST_INTEGER_VALUETYPE; + I <= MVT::LAST_INTEGER_VALUETYPE; ++I) { + MVT VT = MVT::SimpleValueType(I); + if (isTypeLegal(VT)) { + // No support at all + setOperationAction(ISD::SDIVREM, VT, Expand); + setOperationAction(ISD::UDIVREM, VT, Expand); + } + } + + if (Subtarget.hasMul32()) + setOperationAction(ISD::MUL, MVT::i32, Legal); + else + setOperationAction(ISD::MUL, MVT::i32, Expand); + + if (Subtarget.hasMul32High()) { + setOperationAction(ISD::MULHU, MVT::i32, Legal); + setOperationAction(ISD::MULHS, MVT::i32, Legal); + } else { + setOperationAction(ISD::MULHU, MVT::i32, Expand); + setOperationAction(ISD::MULHS, MVT::i32, Expand); + } + setOperationAction(ISD::MUL, MVT::i64, Expand); + setOperationAction(ISD::MULHS, MVT::i64, Expand); + setOperationAction(ISD::MULHU, MVT::i64, Expand); + + if (Subtarget.hasDiv32()) { + setOperationAction(ISD::SDIV, MVT::i32, Legal); + setOperationAction(ISD::UDIV, MVT::i32, Legal); + setOperationAction(ISD::SREM, MVT::i32, Legal); + setOperationAction(ISD::UREM, MVT::i32, Legal); + } else { + setOperationAction(ISD::SDIV, MVT::i32, Expand); + setOperationAction(ISD::UDIV, MVT::i32, Expand); + setOperationAction(ISD::SREM, MVT::i32, Expand); + setOperationAction(ISD::UREM, MVT::i32, Expand); + } + + setOperationAction(ISD::SDIV, MVT::i64, Expand); + setOperationAction(ISD::UDIV, MVT::i64, Expand); + setOperationAction(ISD::SREM, MVT::i64, Expand); + setOperationAction(ISD::UREM, MVT::i64, Expand); + + // Xtensa doesn't support [ADD,SUB][E,C] + setOperationAction(ISD::ADDC, MVT::i32, Expand); + setOperationAction(ISD::ADDE, MVT::i32, Expand); + setOperationAction(ISD::SUBC, MVT::i32, Expand); + setOperationAction(ISD::SUBE, MVT::i32, Expand); + + setOperationAction(ISD::ADD, MVT::i64, Expand); + setOperationAction(ISD::SUB, MVT::i64, Expand); + + // Xtensa doesn't support s[hl,rl,ra]_parts + setOperationAction(ISD::SHL_PARTS, MVT::i32, Custom); + setOperationAction(ISD::SRA_PARTS, MVT::i32, Custom); + setOperationAction(ISD::SRL_PARTS, MVT::i32, Custom); + + // Funnel shifts + setOperationAction(ISD::FSHR, MVT::i32, Custom); + setOperationAction(ISD::FSHL, MVT::i32, Custom); + + // Bit Manipulation + setOperationAction(ISD::BSWAP, MVT::i32, Expand); + setOperationAction(ISD::BSWAP, MVT::i64, Expand); + setOperationAction(ISD::ROTL, MVT::i32, Expand); + setOperationAction(ISD::ROTR, MVT::i32, Expand); + setOperationAction(ISD::CTPOP, MVT::i32, Expand); + setOperationAction(ISD::CTTZ, MVT::i32, Expand); + setOperationAction(ISD::CTLZ, MVT::i32, Expand); + setOperationAction(ISD::CTTZ_ZERO_UNDEF, MVT::i32, Expand); + setOperationAction(ISD::CTLZ_ZERO_UNDEF, MVT::i32, Expand); + + setOperationAction(ISD::SMUL_LOHI, MVT::i32, Expand); + setOperationAction(ISD::SMUL_LOHI, MVT::i64, Expand); + setOperationAction(ISD::UMUL_LOHI, MVT::i32, Expand); + setOperationAction(ISD::UMUL_LOHI, MVT::i64, Expand); + + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i8, Expand); + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i16, Expand); + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i32, Expand); + + // Handle floating-point types. + for (unsigned I = MVT::FIRST_FP_VALUETYPE; I <= MVT::LAST_FP_VALUETYPE; ++I) { + MVT VT = MVT::SimpleValueType(I); + if (isTypeLegal(VT)) { + if (VT.getSizeInBits() == 32 && Subtarget.hasSingleFloat()) { + setOperationAction(ISD::FABS, VT, Legal); + setOperationAction(ISD::FADD, VT, Legal); + setOperationAction(ISD::FMA, VT, Legal); + setOperationAction(ISD::FMUL, VT, Legal); + setOperationAction(ISD::FNEG, VT, Legal); + setOperationAction(ISD::FSUB, VT, Legal); + } else { + setOperationAction(ISD::FABS, VT, Expand); + setOperationAction(ISD::FADD, VT, Expand); + setOperationAction(ISD::FMA, VT, Expand); + setOperationAction(ISD::FMUL, VT, Expand); + setOperationAction(ISD::FNEG, VT, Expand); + setOperationAction(ISD::FSUB, VT, Expand); + } + + // No special instructions for these. + setOperationAction(ISD::FCBRT, VT, Expand); + setOperationAction(ISD::FCEIL, VT, Expand); + setOperationAction(ISD::FCOPYSIGN, VT, Expand); + setOperationAction(ISD::FCOS, VT, Expand); + setOperationAction(ISD::FDIV, VT, Expand); + setOperationAction(ISD::FEXP, VT, Expand); + setOperationAction(ISD::FEXP2, VT, Expand); + setOperationAction(ISD::FFLOOR, VT, Expand); + setOperationAction(ISD::FLOG, VT, Expand); + setOperationAction(ISD::FLOG2, VT, Expand); + setOperationAction(ISD::FLOG10, VT, Expand); + setOperationAction(ISD::FMAXIMUM, VT, Expand); + setOperationAction(ISD::FMINIMUM, VT, Expand); + setOperationAction(ISD::FMAXNUM, VT, Expand); + setOperationAction(ISD::FMINNUM, VT, Expand); + setOperationAction(ISD::FNEARBYINT, VT, Expand); + setOperationAction(ISD::FPOW, VT, Expand); + setOperationAction(ISD::FPOWI, VT, Expand); + setOperationAction(ISD::FREM, VT, Expand); + setOperationAction(ISD::FRINT, VT, Expand); + setOperationAction(ISD::FROUND, VT, Expand); + setOperationAction(ISD::FSIN, VT, Expand); + setOperationAction(ISD::FSINCOS, VT, Expand); + setOperationAction(ISD::FSQRT, VT, Expand); + setOperationAction(ISD::FTRUNC, VT, Expand); + setOperationAction(ISD::LLRINT, VT, Expand); + setOperationAction(ISD::LLROUND, VT, Expand); + setOperationAction(ISD::LRINT, VT, Expand); + setOperationAction(ISD::LROUND, VT, Expand); + } + } + + if (Subtarget.hasSingleFloat()) { + setOperationAction(ISD::BITCAST, MVT::i32, Legal); + setOperationAction(ISD::BITCAST, MVT::f32, Legal); + setOperationAction(ISD::UINT_TO_FP, MVT::i32, Legal); + setOperationAction(ISD::SINT_TO_FP, MVT::i32, Legal); + setOperationAction(ISD::FP_TO_UINT, MVT::i32, Legal); + setOperationAction(ISD::FP_TO_SINT, MVT::i32, Legal); + } else { + setOperationAction(ISD::BITCAST, MVT::i32, Expand); + setOperationAction(ISD::BITCAST, MVT::f32, Expand); + setOperationAction(ISD::UINT_TO_FP, MVT::i32, Expand); + setOperationAction(ISD::SINT_TO_FP, MVT::i32, Expand); + setOperationAction(ISD::FP_TO_UINT, MVT::i32, Expand); + setOperationAction(ISD::FP_TO_SINT, MVT::i32, Expand); + } + + setOperationAction(ISD::UINT_TO_FP, MVT::i64, Expand); + setOperationAction(ISD::SINT_TO_FP, MVT::i64, Expand); + setOperationAction(ISD::FP_TO_UINT, MVT::i64, Expand); + setOperationAction(ISD::FP_TO_SINT, MVT::i64, Expand); + + setOperationAction(ISD::SETCC, MVT::f64, Expand); + setOperationAction(ISD::BITCAST, MVT::i64, Expand); + setOperationAction(ISD::BITCAST, MVT::f64, Expand); + + if (Subtarget.hasSingleFloat()) { + setCondCodeAction(ISD::SETOGT, MVT::f32, Expand); + setCondCodeAction(ISD::SETOGE, MVT::f32, Expand); + setCondCodeAction(ISD::SETONE, MVT::f32, Expand); + setCondCodeAction(ISD::SETUGE, MVT::f32, Expand); + setCondCodeAction(ISD::SETUGT, MVT::f32, Expand); + + setTargetDAGCombine(ISD::FADD); + setTargetDAGCombine(ISD::FSUB); + } + + if (Subtarget.hasSingleFloat()) { + setTargetDAGCombine(ISD::BRCOND); + } + + if (Subtarget.hasLoop()) { + setTargetDAGCombine(ISD::BR_CC); + } + + // Needed so that we don't try to implement f128 constant loads using + // a load-and-extend of a f80 constant (in cases where the constant + // would fit in an f80). + for (MVT VT : MVT::fp_valuetypes()) + setLoadExtAction(ISD::EXTLOAD, VT, MVT::f80, Expand); + + // Floating-point truncation and stores need to be done separately. + setTruncStoreAction(MVT::f64, MVT::f32, Expand); + + // Implement custom stack allocations + setOperationAction(ISD::DYNAMIC_STACKALLOC, PtrVT, Custom); + // Implement custom stack save and restore + setOperationAction(ISD::STACKSAVE, MVT::Other, Custom); + setOperationAction(ISD::STACKRESTORE, MVT::Other, Custom); + + // VASTART and VACOPY need to deal with the Xtensa-specific varargs + // structure, but VAEND is a no-op. + setOperationAction(ISD::VASTART, MVT::Other, Custom); + // we use special va_list structure so we have to customize this + setOperationAction(ISD::VAARG, MVT::Other, Expand); + setOperationAction(ISD::VACOPY, MVT::Other, Custom); + setOperationAction(ISD::VAEND, MVT::Other, Expand); + + setOperationAction(ISD::TRAP, MVT::Other, Legal); + + // to have the best chance and doing something good with fences custom lower + // them + setOperationAction(ISD::ATOMIC_FENCE, MVT::Other, Custom); + + if (!Subtarget.hasS32C1I()) { + for (unsigned I = MVT::FIRST_INTEGER_VALUETYPE; + I <= MVT::LAST_INTEGER_VALUETYPE; ++I) { + MVT VT = MVT::SimpleValueType(I); + if (isTypeLegal(VT)) { + setOperationAction(ISD::ATOMIC_CMP_SWAP, VT, Expand); + setOperationAction(ISD::ATOMIC_SWAP, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_ADD, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_SUB, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_AND, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_OR, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_XOR, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_NAND, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_MIN, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_MAX, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_UMIN, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_UMAX, VT, Expand); + } + } + } + + // Compute derived properties from the register classes + computeRegisterProperties(STI.getRegisterInfo()); +} + +/// Return the register type for a given MVT +MVT XtensaTargetLowering::getRegisterTypeForCallingConv(LLVMContext &Context, + CallingConv::ID CC, + EVT VT) const { + if (VT.isFloatingPoint()) + return MVT::i32; + + return TargetLowering::getRegisterTypeForCallingConv(Context, CC, VT); +} + +bool XtensaTargetLowering::isFMAFasterThanFMulAndFAdd(const MachineFunction &MF, + EVT VT) const { + if (!VT.isSimple()) + return false; + + switch (VT.getSimpleVT().SimpleTy) { + case MVT::f32: + return Subtarget.hasSingleFloat(); + default: + break; + } + + return false; +} + +/// If a physical register, this returns the register that receives the +/// exception address on entry to an EH pad. +Register XtensaTargetLowering::getExceptionPointerRegister( + const Constant *PersonalityFn) const { + return Xtensa::A2; +} + +/// If a physical register, this returns the register that receives the +/// exception typeid on entry to a landing pad. +Register XtensaTargetLowering::getExceptionSelectorRegister( + const Constant *PersonalityFn) const { + return Xtensa::A3; +} + +bool XtensaTargetLowering::isOffsetFoldingLegal( + const GlobalAddressSDNode *GA) const { + // The Xtensa target isn't yet aware of offsets. + return false; +} + +bool XtensaTargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT, + bool ForCodeSize) const { + return false; +} + +unsigned XtensaTargetLowering::getVaListSizeInBits(const DataLayout &DL) const { + // 2 * sizeof(int*) + sizeof(int) + return 3 * 4; +} + +//===----------------------------------------------------------------------===// +// Inline asm support +//===----------------------------------------------------------------------===// +TargetLowering::ConstraintType +XtensaTargetLowering::getConstraintType(StringRef Constraint) const { + if (Constraint.size() == 1) { + switch (Constraint[0]) { + case 'a': + case 'd': + case 'f': + case 'r': + return C_RegisterClass; + + default: + break; + } + } + return TargetLowering::getConstraintType(Constraint); +} + +TargetLowering::ConstraintWeight +XtensaTargetLowering::getSingleConstraintMatchWeight( + AsmOperandInfo &info, const char *constraint) const { + ConstraintWeight weight = CW_Invalid; + Value *CallOperandVal = info.CallOperandVal; + // If we don't have a value, we can't do a match, + // but allow it at the lowest weight. + if (CallOperandVal == NULL) + return CW_Default; + + Type *type = CallOperandVal->getType(); + + // Look at the constraint type. + switch (*constraint) { + default: + weight = TargetLowering::getSingleConstraintMatchWeight(info, constraint); + break; + + case 'a': + case 'd': + case 'r': + if (type->isIntegerTy()) + weight = CW_Register; + break; + case 'f': + if (type->isFloatingPointTy()) + weight = CW_Register; + break; + + } + return weight; +} + +std::pair +XtensaTargetLowering::getRegForInlineAsmConstraint( + const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const { + if (Constraint.size() == 1) { + // GCC Constraint Letters + switch (Constraint[0]) { + default: + break; + case 'a': // Address register + case 'd': // Data register (equivalent to 'r') + case 'r': // General-purpose register + return std::make_pair(0U, &Xtensa::ARRegClass); + case 'f': // Floating-point register + if (Subtarget.hasSingleFloat()) + return std::make_pair(0U, &Xtensa::FPRRegClass); + } + } + return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); +} + +/// LowerAsmOperandForConstraint - Lower the specified operand into the Ops +/// vector. If it is invalid, don't add anything to Ops. +void XtensaTargetLowering::LowerAsmOperandForConstraint( + SDValue Op, std::string &Constraint, std::vector &Ops, + SelectionDAG &DAG) const { + SDLoc DL(Op); + + // Only support length 1 constraints for now. + if (Constraint.length() > 1) + return; + + TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG); +} + +//===----------------------------------------------------------------------===// +// DAG Combine functions +//===----------------------------------------------------------------------===// + +static SDValue performMADD_MSUBCombine(SDNode *ROOTNode, SelectionDAG &CurDAG, + const XtensaSubtarget &Subtarget) { + if (ROOTNode->getOperand(0).getValueType() != MVT::f32) + return SDValue(); + + if (ROOTNode->getOperand(0).getOpcode() != ISD::FMUL && + ROOTNode->getOperand(1).getOpcode() != ISD::FMUL) + return SDValue(); + + SDValue Mult = ROOTNode->getOperand(0).getOpcode() == ISD::FMUL + ? ROOTNode->getOperand(0) + : ROOTNode->getOperand(1); + + SDValue AddOperand = ROOTNode->getOperand(0).getOpcode() == ISD::FMUL + ? ROOTNode->getOperand(1) + : ROOTNode->getOperand(0); + + if (!Mult.hasOneUse()) + return SDValue(); + + SDLoc DL(ROOTNode); + + bool IsAdd = ROOTNode->getOpcode() == ISD::FADD; + unsigned Opcode = IsAdd ? XtensaISD::MADD : XtensaISD::MSUB; + SDValue MAddOps[3] = {AddOperand, Mult->getOperand(0), Mult->getOperand(1)}; + EVT VTs[3] = {MVT::f32, MVT::f32, MVT::f32}; + SDValue MAdd = CurDAG.getNode(Opcode, DL, VTs, MAddOps); + + return MAdd; +} + +static SDValue performSUBCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const XtensaSubtarget &Subtarget) { + if (DCI.isBeforeLegalizeOps()) { + if (Subtarget.hasSingleFloat() && N->getValueType(0) == MVT::f32) + return performMADD_MSUBCombine(N, DAG, Subtarget); + } + return SDValue(); +} + +static SDValue performADDCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const XtensaSubtarget &Subtarget) { + if (DCI.isBeforeLegalizeOps()) { + if (Subtarget.hasSingleFloat() && N->getValueType(0) == MVT::f32) + return performMADD_MSUBCombine(N, DAG, Subtarget); + } + return SDValue(); +} + +static SDValue SearchLoopIntrinsic(SDValue N, ISD::CondCode &CC, int &Imm, + bool &Negate) { + switch (N->getOpcode()) { + default: + break; + case ISD::XOR: { + if (!isa(N.getOperand(1))) + return SDValue(); + if (!cast(N.getOperand(1))->isOne()) + return SDValue(); + Negate = !Negate; + return SearchLoopIntrinsic(N.getOperand(0), CC, Imm, Negate); + } + case ISD::SETCC: { + auto *Const = dyn_cast(N.getOperand(1)); + if (!Const) + return SDValue(); + if (Const->isNullValue()) + Imm = 0; + else if (Const->isOne()) + Imm = 1; + else + return SDValue(); + CC = cast(N.getOperand(2))->get(); + return SearchLoopIntrinsic(N->getOperand(0), CC, Imm, Negate); + } + case ISD::INTRINSIC_W_CHAIN: { + unsigned IntOp = cast(N.getOperand(1))->getZExtValue(); + if (IntOp != Intrinsic::loop_decrement_reg) + return SDValue(); + return N; + } + } + return SDValue(); +} + +static SDValue PerformHWLoopCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const XtensaSubtarget &Subtarget) { + SDValue Chain = N->getOperand(0); + SDLoc DL(N); + SDValue Cond; + SDValue Dest; + ISD::CondCode CC = ISD::SETEQ; + int Imm = 1; + bool Negate = false; + + assert(N->getOpcode() == ISD::BR_CC && "Expected BR_CC!"); + CC = cast(N->getOperand(1))->get(); + Cond = N->getOperand(2); + Dest = N->getOperand(4); + if (auto *Const = dyn_cast(N->getOperand(3))) { + if (!Const->isOne() && !Const->isNullValue()) + return SDValue(); + Imm = Const->getZExtValue(); + } else + return SDValue(); + + SDValue Int = SearchLoopIntrinsic(Cond, CC, Imm, Negate); + if (Int) { + assert((N->hasOneUse() && N->use_begin()->getOpcode() == ISD::BR) && + "expected single br user"); + SDNode *Br = *N->use_begin(); + SDValue OtherTarget = Br->getOperand(1); + + if (Negate) + CC = ISD::getSetCCInverse(CC, /* Integer inverse */ MVT::i32); + + auto IsTrueIfZero = [](ISD::CondCode CC, int Imm) { + return (CC == ISD::SETEQ && Imm == 0) || (CC == ISD::SETNE && Imm == 1) || + (CC == ISD::SETLT && Imm == 1) || (CC == ISD::SETULT && Imm == 1); + }; + + auto IsFalseIfZero = [](ISD::CondCode CC, int Imm) { + return (CC == ISD::SETEQ && Imm == 1) || (CC == ISD::SETNE && Imm == 0) || + (CC == ISD::SETGT && Imm == 0) || + (CC == ISD::SETUGT && Imm == 0) || + (CC == ISD::SETGE && Imm == 1) || (CC == ISD::SETUGE && Imm == 1); + }; + + if (IsTrueIfZero(CC, Imm)) { + SDValue NewBrOps[] = {Br->getOperand(0), Dest}; + SDValue NewBr = DAG.getNode(ISD::BR, SDLoc(Br), MVT::Other, NewBrOps); + DAG.ReplaceAllUsesOfValueWith(SDValue(Br, 0), NewBr); + Dest = OtherTarget; + } else if (!IsFalseIfZero(CC, Imm)) { + llvm_unreachable("unsupported condition"); + } + SDLoc dl(Int); + SDValue Elements = Int.getOperand(2); + SDValue Size = DAG.getTargetConstant( + cast(Int.getOperand(3))->getZExtValue(), dl, MVT::i32); + SDValue Args[] = { + Int.getOperand(0), + Elements, + Size, + }; + SDValue LoopDec = DAG.getNode(XtensaISD::LOOPDEC, dl, + DAG.getVTList(MVT::i32, MVT::Other), Args); + + // We now need to make the intrinsic dead (it cannot be instruction + // selected). + DAG.ReplaceAllUsesWith(Int.getNode(), LoopDec.getNode()); + + Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, + SDValue(LoopDec.getNode(), 1), Chain); + + SDValue EndArgs[] = {Chain, SDValue(LoopDec.getNode(), 0), Dest}; + return DAG.getNode(XtensaISD::LOOPBR, dl, MVT::Other, EndArgs); + } + return SDValue(); +} + +static SDValue PerformBRCONDCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const XtensaSubtarget &Subtarget) { + SDValue Chain = N->getOperand(0); + SDLoc DL(N); + SDValue Cond = N->getOperand(1); + SDValue Dest = N->getOperand(2); + ISD::CondCode CC = ISD::SETEQ; + + if (Cond.getOpcode() != ISD::SETCC) + return SDValue(); + + CC = cast(Cond->getOperand(2))->get(); + SDValue LHS = Cond->getOperand(0); + SDValue RHS = Cond->getOperand(1); + + if (LHS.getValueType() != MVT::i32) + return SDValue(); + + return DAG.getNode(ISD::BR_CC, DL, MVT::isVoid, Chain, DAG.getCondCode(CC), + LHS, RHS, Dest); +} + +SDValue XtensaTargetLowering::PerformDAGCombine(SDNode *N, + DAGCombinerInfo &DCI) const { + SelectionDAG &DAG = DCI.DAG; + unsigned Opc = N->getOpcode(); + + switch (Opc) { + default: + break; + case ISD::FADD: + return performADDCombine(N, DAG, DCI, Subtarget); + case ISD::FSUB: + return performSUBCombine(N, DAG, DCI, Subtarget); + case ISD::BR_CC: + return PerformHWLoopCombine(N, DAG, DCI, Subtarget); + case ISD::BRCOND: + return PerformBRCONDCombine(N, DAG, DCI, Subtarget); + } + + return SDValue(); +} + +//===----------------------------------------------------------------------===// +// Calling conventions +//===----------------------------------------------------------------------===// + +#include "XtensaGenCallingConv.inc" + +static bool CC_Xtensa_Custom(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, + ISD::ArgFlagsTy ArgFlags, CCState &State) { + static const MCPhysReg IntRegs[] = {Xtensa::A2, Xtensa::A3, Xtensa::A4, + Xtensa::A5, Xtensa::A6, Xtensa::A7}; + + if (ArgFlags.isByVal()) { + Align ByValAlign = ArgFlags.getNonZeroByValAlign(); + unsigned Offset = State.AllocateStack(ArgFlags.getByValSize(), ByValAlign); + State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo)); + // Allocate rest of registers, because rest part is not used to pass + // arguments + while (State.AllocateReg(IntRegs)) { + } + return false; + } + + // Promote i8 and i16 + if (LocVT == MVT::i8 || LocVT == MVT::i16) { + LocVT = MVT::i32; + if (ArgFlags.isSExt()) + LocInfo = CCValAssign::SExt; + else if (ArgFlags.isZExt()) + LocInfo = CCValAssign::ZExt; + else + LocInfo = CCValAssign::AExt; + } + + unsigned Reg; + + Align OrigAlign = ArgFlags.getNonZeroOrigAlign(); + bool needs64BitAlign = (ValVT == MVT::i32 && OrigAlign == Align(8)); + bool needs128BitAlign = (ValVT == MVT::i32 && OrigAlign == Align(16)); + + if (ValVT == MVT::i32 || ValVT == MVT::f32) { + Reg = State.AllocateReg(IntRegs); + // If this is the first part of an i64 arg, + // the allocated register must be either A2, A4 or A6. + if (needs64BitAlign && (Reg == Xtensa::A3 || Reg == Xtensa::A5 || Reg == Xtensa::A7)) + Reg = State.AllocateReg(IntRegs); + // arguments with 16byte alignment must be passed in the first register or passed via stack + if (needs128BitAlign && Reg != Xtensa::A2) + while ( (Reg = State.AllocateReg(IntRegs)) ) { + } + LocVT = MVT::i32; + } else if (ValVT == MVT::f64) { + // Allocate int register and shadow next int register. + Reg = State.AllocateReg(IntRegs); + if (Reg == Xtensa::A3 || Reg == Xtensa::A5 || Reg == Xtensa::A7) + Reg = State.AllocateReg(IntRegs); + State.AllocateReg(IntRegs); + LocVT = MVT::i32; + } else + llvm_unreachable("Cannot handle this ValVT."); + + if (!Reg) { + unsigned Offset = State.AllocateStack(ValVT.getStoreSize(), OrigAlign); + State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo)); + } else + State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); + + return false; +} + +CCAssignFn *XtensaTargetLowering::CCAssignFnForCall(CallingConv::ID CC, + bool IsVarArg) const { + return CC_Xtensa_Custom; +} + +// Value is a value that has been passed to us in the location described by VA +// (and so has type VA.getLocVT()). Convert Value to VA.getValVT(), chaining +// any loads onto Chain. +static SDValue convertLocVTToValVT(SelectionDAG &DAG, const SDLoc &DL, + CCValAssign &VA, SDValue Chain, + SDValue Value) { + // If the argument has been promoted from a smaller type, insert an + // assertion to capture this. + if (VA.getLocInfo() == CCValAssign::SExt) + Value = DAG.getNode(ISD::AssertSext, DL, VA.getLocVT(), Value, + DAG.getValueType(VA.getValVT())); + else if (VA.getLocInfo() == CCValAssign::ZExt) + Value = DAG.getNode(ISD::AssertZext, DL, VA.getLocVT(), Value, + DAG.getValueType(VA.getValVT())); + + if (VA.isExtInLoc()) + Value = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), Value); + else if (VA.getLocInfo() == CCValAssign::Indirect) + Value = DAG.getLoad(VA.getValVT(), DL, Chain, Value, MachinePointerInfo()); + else if (VA.getValVT() == MVT::f32) + Value = DAG.getNode(ISD::BITCAST, DL, VA.getValVT(), Value); + else + assert(VA.getLocInfo() == CCValAssign::Full && "Unsupported getLocInfo"); + return Value; +} + +// Value is a value of type VA.getValVT() that we need to copy into +// the location described by VA. Return a copy of Value converted to +// VA.getValVT(). The caller is responsible for handling indirect values. +static SDValue convertValVTToLocVT(SelectionDAG &DAG, SDLoc DL, CCValAssign &VA, + SDValue Value) { + switch (VA.getLocInfo()) { + case CCValAssign::SExt: + return DAG.getNode(ISD::SIGN_EXTEND, DL, VA.getLocVT(), Value); + case CCValAssign::ZExt: + return DAG.getNode(ISD::ZERO_EXTEND, DL, VA.getLocVT(), Value); + case CCValAssign::AExt: + return DAG.getNode(ISD::ANY_EXTEND, DL, VA.getLocVT(), Value); + case CCValAssign::BCvt: + return DAG.getNode(ISD::BITCAST, DL, VA.getLocVT(), Value); + case CCValAssign::Full: + return Value; + default: + llvm_unreachable("Unhandled getLocInfo()"); + } +} + +SDValue XtensaTargetLowering::LowerFormalArguments( + SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Ins, const SDLoc &DL, + SelectionDAG &DAG, SmallVectorImpl &InVals) const { + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + XtensaFunctionInfo *XtensaFI = MF.getInfo(); + EVT PtrVT = getPointerTy(MF.getDataLayout()); + + XtensaFI->setVarArgsFrameIndex(0); + + // Used with vargs to acumulate store chains. + std::vector OutChains; + + // Assign locations to all of the incoming arguments. + SmallVector ArgLocs; + CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs, + *DAG.getContext()); + + CCInfo.AnalyzeFormalArguments(Ins, CCAssignFnForCall(CallConv, IsVarArg)); + + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + // Arguments stored on registers + if (VA.isRegLoc()) { + EVT RegVT = VA.getLocVT(); + const TargetRegisterClass *RC; + + if (RegVT == MVT::i32) { + RC = &Xtensa::ARRegClass; + } else + llvm_unreachable("RegVT not supported by FormalArguments Lowering"); + + // Transform the arguments stored on + // physical registers into virtual ones + unsigned Reg = 0; + unsigned FrameReg = Subtarget.getRegisterInfo()->getFrameRegister(MF); + + // Argument passed in FrameReg in WinABI we save in A8 (in emitPrologue), + // so load argument from A8 + if (Subtarget.isWinABI() && (VA.getLocReg() == FrameReg)) { + Reg = MF.addLiveIn(Xtensa::A8, RC); + XtensaFI->setSaveFrameRegister(); + } else { + Reg = MF.addLiveIn(VA.getLocReg(), RC); + } + + SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, RegVT); + + // If this is an 8 or 16-bit value, it has been passed promoted + // to 32 bits. Insert an assert[sz]ext to capture this, then + // truncate to the right size. + if (VA.getLocInfo() != CCValAssign::Full) { + unsigned Opcode = 0; + if (VA.getLocInfo() == CCValAssign::SExt) + Opcode = ISD::AssertSext; + else if (VA.getLocInfo() == CCValAssign::ZExt) + Opcode = ISD::AssertZext; + if (Opcode) + ArgValue = DAG.getNode(Opcode, DL, RegVT, ArgValue, + DAG.getValueType(VA.getValVT())); + if (VA.getValVT() == MVT::f32) + ArgValue = DAG.getNode(ISD::BITCAST, DL, VA.getValVT(), ArgValue); + else + ArgValue = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), ArgValue); + } + + InVals.push_back(ArgValue); + + } else { // !VA.isRegLoc() + // sanity check + assert(VA.isMemLoc()); + + EVT ValVT = VA.getValVT(); + + // The stack pointer offset is relative to the caller stack frame. + int FI = MFI.CreateFixedObject(ValVT.getSizeInBits() / 8, + VA.getLocMemOffset(), true); + + if (Ins[VA.getValNo()].Flags.isByVal()) { + // Assume that in this case load operation is created + SDValue FIN = DAG.getFrameIndex(FI, MVT::i32); + InVals.push_back(FIN); + } else { + // Create load nodes to retrieve arguments from the stack + SDValue FIN = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); + InVals.push_back(DAG.getLoad( + ValVT, DL, Chain, FIN, + MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI))); + } + } + } + + if (IsVarArg) { + ArrayRef ArgRegs = makeArrayRef(XtensaArgRegs); + unsigned Idx = CCInfo.getFirstUnallocated(ArgRegs); + const TargetRegisterClass *RC = &Xtensa::ARRegClass; + MachineFrameInfo &MFI = MF.getFrameInfo(); + MachineRegisterInfo &RegInfo = MF.getRegInfo(); + unsigned RegSize = 4; + MVT RegTy = MVT::getIntegerVT(RegSize * 8); + + XtensaFI->setVarArgsFirstGPR(Idx + 2); // 2 - number of a2 register + + XtensaFI->setVarArgsStackOffset(MFI.CreateFixedObject( + PtrVT.getSizeInBits() / 8, CCInfo.getNextStackOffset(), true)); + + // Offset of the first variable argument from stack pointer, and size of + // the vararg save area. For now, the varargs save area is either zero or + // large enough to hold a0-a7. + int VaArgOffset, VarArgsSaveSize; + + // If all registers are allocated, then all varargs must be passed on the + // stack and we don't need to save any argregs. + if (ArgRegs.size() == Idx) { + VaArgOffset = CCInfo.getNextStackOffset(); + VarArgsSaveSize = 0; + } else { + VarArgsSaveSize = RegSize * (ArgRegs.size() - Idx); + VaArgOffset = -VarArgsSaveSize; + } + + // Record the frame index of the first variable argument + // which is a value necessary to VASTART. + int FI = MFI.CreateFixedObject(RegSize, VaArgOffset, true); + XtensaFI->setVarArgsFrameIndex(FI); + + // Copy the integer registers that may have been used for passing varargs + // to the vararg save area. + for (unsigned I = Idx; I < ArgRegs.size(); ++I, VaArgOffset += RegSize) { + const unsigned Reg = RegInfo.createVirtualRegister(RC); + unsigned FrameReg = Subtarget.getRegisterInfo()->getFrameRegister(MF); + + // Argument passed in FrameReg we save in A8 (in emitPrologue), + // so load argument from A8 + if (ArgRegs[I] == FrameReg) { + RegInfo.addLiveIn(Xtensa::A8, Reg); + XtensaFI->setSaveFrameRegister(); + } else { + RegInfo.addLiveIn(ArgRegs[I], Reg); + } + + SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, RegTy); + FI = MFI.CreateFixedObject(RegSize, VaArgOffset, true); + SDValue PtrOff = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); + SDValue Store = DAG.getStore(Chain, DL, ArgValue, PtrOff, + MachinePointerInfo::getFixedStack(MF, FI)); + cast(Store.getNode()) + ->getMemOperand() + ->setValue((Value *)nullptr); + OutChains.push_back(Store); + } + } + + // All stores are grouped in one node to allow the matching between + // the size of Ins and InVals. This only happens when on varg functions + if (!OutChains.empty()) { + OutChains.push_back(Chain); + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, OutChains); + } + + return Chain; +} + +SDValue XtensaTargetLowering::getAddrPCRel(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + EVT Ty = Op.getValueType(); + return DAG.getNode(XtensaISD::PCREL_WRAPPER, DL, Ty, Op); +} + +SDValue +XtensaTargetLowering::LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const { + SelectionDAG &DAG = CLI.DAG; + SDLoc &DL = CLI.DL; + SmallVector &Outs = CLI.Outs; + SmallVector &OutVals = CLI.OutVals; + SmallVector &Ins = CLI.Ins; + SDValue Chain = CLI.Chain; + SDValue Callee = CLI.Callee; + bool &IsTailCall = CLI.IsTailCall; + CallingConv::ID CallConv = CLI.CallConv; + bool IsVarArg = CLI.IsVarArg; + + MachineFunction &MF = DAG.getMachineFunction(); + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + const TargetFrameLowering *TFL = Subtarget.getFrameLowering(); + + // TODO: Support tail call optimization. + IsTailCall = false; + + // Analyze the operands of the call, assigning locations to each operand. + SmallVector ArgLocs; + CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); + + CCAssignFn *CC = CCAssignFnForCall(CallConv, IsVarArg); + + CCInfo.AnalyzeCallOperands(Outs, CC); + + // Get a count of how many bytes are to be pushed on the stack. + unsigned NumBytes = CCInfo.getNextStackOffset(); + + unsigned StackAlignment = TFL->getStackAlignment(); + unsigned NextStackOffset = alignTo(NumBytes, StackAlignment); + + Chain = DAG.getCALLSEQ_START(Chain, NextStackOffset, 0, DL); + + // Copy argument values to their designated locations. + std::deque> RegsToPass; + SmallVector MemOpChains; + SDValue StackPtr; + for (unsigned I = 0, E = ArgLocs.size(); I != E; ++I) { + CCValAssign &VA = ArgLocs[I]; + SDValue ArgValue = OutVals[I]; + ISD::ArgFlagsTy Flags = Outs[I].Flags; + + ArgValue = convertValVTToLocVT(DAG, DL, VA, ArgValue); + + if (VA.isRegLoc()) + // Queue up the argument copies and emit them at the end. + RegsToPass.push_back(std::make_pair(VA.getLocReg(), ArgValue)); + else if (Flags.isByVal()) { + assert(VA.isMemLoc()); + assert(Flags.getByValSize() && + "ByVal args of size 0 should have been ignored by front-end."); + assert(!IsTailCall && + "Do not tail-call optimize if there is a byval argument."); + + if (!StackPtr.getNode()) + StackPtr = DAG.getCopyFromReg(Chain, DL, Xtensa::SP, PtrVT); + unsigned Offset = VA.getLocMemOffset(); + SDValue Address = DAG.getNode(ISD::ADD, DL, PtrVT, StackPtr, + DAG.getIntPtrConstant(Offset, DL)); + SDValue SizeNode = DAG.getConstant(Flags.getByValSize(), DL, MVT::i32); + SDValue Memcpy = DAG.getMemcpy( + Chain, DL, Address, ArgValue, SizeNode, Flags.getNonZeroByValAlign(), + /*isVolatile=*/false, /*AlwaysInline=*/false, + /*isTailCall=*/false, MachinePointerInfo(), MachinePointerInfo()); + MemOpChains.push_back(Memcpy); + } else { + assert(VA.isMemLoc() && "Argument not register or memory"); + + // Work out the address of the stack slot. Unpromoted ints and + // floats are passed as right-justified 8-byte values. + if (!StackPtr.getNode()) + StackPtr = DAG.getCopyFromReg(Chain, DL, Xtensa::SP, PtrVT); + unsigned Offset = VA.getLocMemOffset(); + SDValue Address = DAG.getNode(ISD::ADD, DL, PtrVT, StackPtr, + DAG.getIntPtrConstant(Offset, DL)); + + // Emit the store. + MemOpChains.push_back( + DAG.getStore(Chain, DL, ArgValue, Address, MachinePointerInfo())); + } + } + + // Join the stores, which are independent of one another. + if (!MemOpChains.empty()) + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOpChains); + + // Build a sequence of copy-to-reg nodes, chained and glued together. + SDValue Glue; + for (unsigned I = 0, E = RegsToPass.size(); I != E; ++I) { + unsigned Reg = RegsToPass[I].first; + if (Subtarget.isWinABI()) + Reg = toCallerWindow(Reg); + Chain = DAG.getCopyToReg(Chain, DL, Reg, RegsToPass[I].second, Glue); + Glue = Chain.getValue(1); + } + + std::string name; + unsigned char TF = 0; + + // Accept direct calls by converting symbolic call addresses to the + // associated Target* opcodes. + if (ExternalSymbolSDNode *E = dyn_cast(Callee)) { + name = E->getSymbol(); + TF = E->getTargetFlags(); + if (isPositionIndependent()) { + report_fatal_error("PIC relocations is not supported"); + } else + Callee = DAG.getTargetExternalSymbol(E->getSymbol(), PtrVT, TF); + } else if (GlobalAddressSDNode *G = dyn_cast(Callee)) { + // TODO replace GlobalAddress to some special operand instead of + // ExternalSymbol + // Callee = + // DAG.getTargetExternalSymbol(strdup(G->getGlobal()->getName().str().c_str()), + // PtrVT); + + const GlobalValue *GV = G->getGlobal(); + name = GV->getName().str(); + } + + if ((!name.empty()) && isLongCall(name.c_str())) { + // Create a constant pool entry for the callee address + XtensaCP::XtensaCPModifier Modifier = XtensaCP::no_modifier; + + XtensaConstantPoolValue *CPV = XtensaConstantPoolSymbol::Create( + *DAG.getContext(), name.c_str(), 0 /* XtensaCLabelIndex */, false, + Modifier); + + // Get the address of the callee into a register + SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, Align(4), 0, TF); + SDValue CPWrap = getAddrPCRel(CPAddr, DAG); + Callee = CPWrap; + } + + // The first call operand is the chain and the second is the target address. + SmallVector Ops; + Ops.push_back(Chain); + Ops.push_back(Callee); + + // Add a register mask operand representing the call-preserved registers. + const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo(); + const uint32_t *Mask = TRI->getCallPreservedMask(MF, CallConv); + assert(Mask && "Missing call preserved mask for calling convention"); + Ops.push_back(DAG.getRegisterMask(Mask)); + + // Add argument registers to the end of the list so that they are + // known live into the call. + for (unsigned I = 0, E = RegsToPass.size(); I != E; ++I) { + unsigned Reg = RegsToPass[I].first; + if (Subtarget.isWinABI()) + Reg = toCallerWindow(Reg); + Ops.push_back(DAG.getRegister(Reg, RegsToPass[I].second.getValueType())); + } + + // Glue the call to the argument copies, if any. + if (Glue.getNode()) + Ops.push_back(Glue); + + SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + Chain = DAG.getNode(Subtarget.isWinABI() ? XtensaISD::CALLW : XtensaISD::CALL, + DL, NodeTys, Ops); + Glue = Chain.getValue(1); + + // Mark the end of the call, which is glued to the call itself. + Chain = DAG.getCALLSEQ_END(Chain, DAG.getConstant(NumBytes, DL, PtrVT, true), + DAG.getConstant(0, DL, PtrVT, true), Glue, DL); + Glue = Chain.getValue(1); + + // Assign locations to each value returned by this call. + SmallVector RetLocs; + CCState RetCCInfo(CallConv, IsVarArg, MF, RetLocs, *DAG.getContext()); + RetCCInfo.AnalyzeCallResult(Ins, Subtarget.isWinABI() ? RetCCW_Xtensa + : RetCC_Xtensa); + + // Copy all of the result registers out of their specified physreg. + for (unsigned I = 0, E = RetLocs.size(); I != E; ++I) { + CCValAssign &VA = RetLocs[I]; + + // Copy the value out, gluing the copy to the end of the call sequence. + unsigned Reg = VA.getLocReg(); + SDValue RetValue = DAG.getCopyFromReg(Chain, DL, Reg, VA.getLocVT(), Glue); + Chain = RetValue.getValue(1); + Glue = RetValue.getValue(2); + + // Convert the value of the return register into the value that's + // being returned. + InVals.push_back(convertLocVTToValVT(DAG, DL, VA, Chain, RetValue)); + } + return Chain; +} + +bool XtensaTargetLowering::CanLowerReturn( + CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg, + const SmallVectorImpl &Outs, LLVMContext &Context) const { + SmallVector RVLocs; + CCState CCInfo(CallConv, IsVarArg, MF, RVLocs, Context); + return CCInfo.CheckReturn(Outs, RetCC_Xtensa); +} + +SDValue +XtensaTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, + bool IsVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + const SDLoc &DL, SelectionDAG &DAG) const { + MachineFunction &MF = DAG.getMachineFunction(); + + // Assign locations to each returned value. + SmallVector RetLocs; + CCState RetCCInfo(CallConv, IsVarArg, MF, RetLocs, *DAG.getContext()); + RetCCInfo.AnalyzeReturn(Outs, RetCC_Xtensa); + + SDValue Glue; + // Quick exit for void returns + if (RetLocs.empty()) + return DAG.getNode(Subtarget.isWinABI() ? XtensaISD::RETW_FLAG + : XtensaISD::RET_FLAG, + DL, MVT::Other, Chain); + + // Copy the result values into the output registers. + SmallVector RetOps; + RetOps.push_back(Chain); + for (unsigned I = 0, E = RetLocs.size(); I != E; ++I) { + CCValAssign &VA = RetLocs[I]; + SDValue RetValue = OutVals[I]; + + // Make the return register live on exit. + assert(VA.isRegLoc() && "Can only return in registers!"); + + // Promote the value as required. + RetValue = convertValVTToLocVT(DAG, DL, VA, RetValue); + + // Chain and glue the copies together. + unsigned Reg = VA.getLocReg(); + Chain = DAG.getCopyToReg(Chain, DL, Reg, RetValue, Glue); + Glue = Chain.getValue(1); + RetOps.push_back(DAG.getRegister(Reg, VA.getLocVT())); + } + + // Update chain and glue. + RetOps[0] = Chain; + if (Glue.getNode()) + RetOps.push_back(Glue); + + return DAG.getNode(Subtarget.isWinABI() ? XtensaISD::RETW_FLAG + : XtensaISD::RET_FLAG, + DL, MVT::Other, RetOps); +} + +SDValue XtensaTargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const { + SDValue Chain = Op.getOperand(0); + ISD::CondCode CC = cast(Op.getOperand(1))->get(); + SDValue LHS = Op.getOperand(2); + SDValue RHS = Op.getOperand(3); + SDValue Dest = Op.getOperand(4); + SDLoc DL(Op); + + if (LHS.getValueType() == MVT::f32) { + SDValue TargetCC = DAG.getConstant(CC, DL, MVT::i32); + return DAG.getNode(XtensaISD::BR_CC_FP, DL, Op.getValueType(), Chain, + TargetCC, LHS, RHS, Dest); + } else { + llvm_unreachable("invalid BR_CC to lower"); + } +} + +SDValue XtensaTargetLowering::LowerSELECT_CC(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + EVT Ty = Op.getOperand(0).getValueType(); + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + SDValue TrueV = Op.getOperand(2); + SDValue FalseV = Op.getOperand(3); + ISD::CondCode CC = cast(Op->getOperand(4))->get(); + SDValue TargetCC = DAG.getConstant(CC, DL, MVT::i32); + + // Wrap select nodes + if (LHS.getValueType() == MVT::f32) + return DAG.getNode(XtensaISD::SELECT_CC_FP, DL, TrueV.getValueType(), LHS, + RHS, TrueV, FalseV, TargetCC); + else if (TrueV.getValueType() == MVT::f32) + return DAG.getNode(XtensaISD::SELECT_CC_FP, DL, TrueV.getValueType(), LHS, + RHS, TrueV, FalseV, TargetCC); + else + return DAG.getNode(XtensaISD::SELECT_CC, DL, Ty, LHS, RHS, TrueV, FalseV, + TargetCC); +} + +SDValue XtensaTargetLowering::LowerSETCC(SDValue Op, SelectionDAG &DAG) const { + SDLoc DL(Op); + EVT Ty = Op.getOperand(0).getValueType(); + SDValue LHS = Op.getOperand(0); + SDValue RHS = Op.getOperand(1); + ISD::CondCode CC = cast(Op.getOperand(2))->get(); + SDValue TargetCC = DAG.getConstant(CC, DL, MVT::i32); + + // Check Op SDNode users + // If there are only CALL/CALLW nodes, don't expand Global Address + SDNode &OpNode = *Op.getNode(); + bool Val = false; + for (SDNode::use_iterator UI = OpNode.use_begin(); UI != OpNode.use_end(); + ++UI) { + SDNode &User = *UI.getUse().getUser(); + unsigned OpCode = User.getOpcode(); + if (OpCode == ISD::BRCOND) { + Val = true; + break; + } + } + + // SETCC has BRCOND predecessor, return original operation + if (Val) + return Op; + + // Expand to target SELECT_CC + SDValue TrueV = DAG.getConstant(1, DL, Op.getValueType()); + SDValue FalseV = DAG.getConstant(0, DL, Op.getValueType()); + + if (LHS.getValueType() == MVT::f32) + return DAG.getNode(XtensaISD::SELECT_CC_FP, DL, TrueV.getValueType(), LHS, + RHS, TrueV, FalseV, TargetCC); + else if (TrueV.getValueType() == MVT::f32) + return DAG.getNode(XtensaISD::SELECT_CC_FP, DL, TrueV.getValueType(), LHS, + RHS, TrueV, FalseV, TargetCC); + else + return DAG.getNode(XtensaISD::SELECT_CC, DL, Ty, LHS, RHS, TrueV, FalseV, + TargetCC); +} + +SDValue XtensaTargetLowering::LowerRETURNADDR(SDValue Op, + SelectionDAG &DAG) const { + // check the depth + // TODO: xtensa-gcc can handle this, by navigating through the stack, we + // should be able to do this too + assert((cast(Op.getOperand(0))->getZExtValue() == 0) && + "Return address can be determined only for current frame."); + + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MVT VT = Op.getSimpleValueType(); + unsigned RA = Xtensa::A0; + MFI.setReturnAddressIsTaken(true); + + // Return RA, which contains the return address. Mark it an implicit + // live-in. + unsigned Reg = MF.addLiveIn(RA, getRegClassFor(VT)); + return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), Reg, VT); +} + +SDValue XtensaTargetLowering::LowerImmediate(SDValue Op, + SelectionDAG &DAG) const { + const ConstantSDNode *CN = cast(Op); + SDLoc DL(CN); + APInt apval = CN->getAPIntValue(); + int64_t value = apval.getSExtValue(); + if (Op.getValueType() == MVT::i32) { + if (value > -2048 && value <= 2047) + return Op; + Type *Ty = Type::getInt32Ty(*DAG.getContext()); + Constant *CV = ConstantInt::get(Ty, value); + SDValue CP = DAG.getConstantPool(CV, MVT::i32); + return CP; + } + return Op; +} + +SDValue XtensaTargetLowering::LowerImmediateFP(SDValue Op, + SelectionDAG &DAG) const { + const ConstantFPSDNode *CN = cast(Op); + SDLoc DL(CN); + APFloat apval = CN->getValueAPF(); + int64_t value = FloatToBits(CN->getValueAPF().convertToFloat()); + if (Op.getValueType() == MVT::f32) { + Type *Ty = Type::getInt32Ty(*DAG.getContext()); + Constant *CV = ConstantInt::get(Ty, value); + SDValue CP = DAG.getConstantPool(CV, MVT::i32); + return DAG.getNode(ISD::BITCAST, DL, MVT::f32, CP); + } + return Op; +} + +SDValue XtensaTargetLowering::LowerGlobalAddress(SDValue Op, + SelectionDAG &DAG) const { + // Reloc::Model RM = DAG.getTarget().getRelocationModel(); + SDLoc DL(Op); + + if (GlobalAddressSDNode *G = dyn_cast(Op)) { + auto PtrVt = getPointerTy(DAG.getDataLayout()); + const GlobalValue *GV = G->getGlobal(); + + // Check Op SDNode users + // If there are only CALL/CALLW nodes, don't expand Global Address + SDNode &OpNode = *Op.getNode(); + bool Val = false; + for (SDNode::use_iterator UI = OpNode.use_begin(); UI != OpNode.use_end(); + ++UI) { + SDNode &User = *UI.getUse().getUser(); + unsigned OpCode = User.getOpcode(); + if (OpCode != XtensaISD::CALL && OpCode != XtensaISD::CALLW) { + Val = true; + break; + } + } + if (!Val) { + SDValue TargAddr = DAG.getTargetGlobalAddress(G->getGlobal(), DL, PtrVt, + 0, 0 /* TargetFlags */); + return TargAddr; + } + + SDValue CPAddr = DAG.getTargetConstantPool(GV, PtrVt, Align(4)); + SDValue CPWrap = getAddrPCRel(CPAddr, DAG); + + return CPWrap; + } + llvm_unreachable("invalid global addresses to lower"); +} + +SDValue XtensaTargetLowering::LowerGlobalTLSAddress(GlobalAddressSDNode *GA, + SelectionDAG &DAG) const { + SDLoc DL(GA); + const GlobalValue *GV = GA->getGlobal(); + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + + if (DAG.getTarget().useEmulatedTLS()) + return LowerToTLSEmulatedModel(GA, DAG); + + TLSModel::Model model = getTargetMachine().getTLSModel(GV); + + if (!Subtarget.hasTHREADPTR()) { + llvm_unreachable("only emulated TLS supported"); + } + + if ((model == TLSModel::LocalExec) || (model == TLSModel::InitialExec)) { + auto PtrVt = getPointerTy(DAG.getDataLayout()); + + bool Priv = GV->isPrivateLinkage(GV->getLinkage()); + // Create a constant pool entry for the callee address + XtensaConstantPoolValue *CPV = XtensaConstantPoolSymbol::Create( + *DAG.getContext(), GV->getName().str().c_str() /* Sym */, + 0 /* XtensaCLabelIndex */, Priv, XtensaCP::TPOFF); + + // Get the address of the callee into a register + SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVt, Align(4)); + SDValue CPWrap = getAddrPCRel(CPAddr, DAG); + + SDValue TPRegister = DAG.getRegister(Xtensa::THREADPTR, MVT::i32); + SDValue ThreadPointer = + DAG.getNode(XtensaISD::RUR, DL, MVT::i32, TPRegister); + return DAG.getNode(ISD::ADD, DL, PtrVT, ThreadPointer, CPWrap); + } else + llvm_unreachable("only local-exec and initial-exec TLS mode supported"); + + return SDValue(); +} + +SDValue XtensaTargetLowering::LowerBlockAddress(BlockAddressSDNode *Node, + SelectionDAG &DAG) const { + const BlockAddress *BA = Node->getBlockAddress(); + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + + XtensaConstantPoolValue *CPV = + XtensaConstantPoolConstant::Create(BA, 0, XtensaCP::CPBlockAddress, 0); + SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVT, Align(4)); + + SDValue CPWrap = getAddrPCRel(CPAddr, DAG); + return CPWrap; +} + +SDValue XtensaTargetLowering::LowerBR_JT(SDValue Op, SelectionDAG &DAG) const { + SDValue Chain = Op.getOperand(0); + SDValue Table = Op.getOperand(1); + SDValue Index = Op.getOperand(2); + SDLoc DL(Op); + JumpTableSDNode *JT = cast(Table); + MachineFunction &MF = DAG.getMachineFunction(); + const MachineJumpTableInfo *MJTI = MF.getJumpTableInfo(); + + SDValue TargetJT = DAG.getTargetJumpTable(JT->getIndex(), MVT::i32); + + const DataLayout &TD = DAG.getDataLayout(); + EVT PTy = getPointerTy(TD); + + unsigned EntrySize = MJTI->getEntrySize(TD); + + Index = DAG.getNode(ISD::MUL, DL, Index.getValueType(), Index, + DAG.getConstant(EntrySize, DL, Index.getValueType())); + SDValue Addr = DAG.getNode(ISD::ADD, DL, Index.getValueType(), Index, Table); + + EVT MemVT = EVT::getIntegerVT(*DAG.getContext(), EntrySize * 8); + SDValue LD = DAG.getExtLoad(ISD::SEXTLOAD, DL, PTy, Chain, Addr, + MachinePointerInfo::getJumpTable(MF), MemVT); + Addr = LD; + + return DAG.getNode(XtensaISD::BR_JT, DL, MVT::Other, LD.getValue(1), Addr, + TargetJT); +} + +SDValue XtensaTargetLowering::LowerJumpTable(JumpTableSDNode *JT, + SelectionDAG &DAG) const { + SDLoc DL(JT); + EVT PtrVt = getPointerTy(DAG.getDataLayout()); + + // Create a constant pool entry for the callee address + XtensaConstantPoolValue *CPV = + XtensaConstantPoolJumpTable::Create(*DAG.getContext(), JT->getIndex()); + + // Get the address of the callee into a register + SDValue CPAddr = DAG.getTargetConstantPool(CPV, PtrVt, Align(4)); + SDValue CPWrap = getAddrPCRel(CPAddr, DAG); + + return CPWrap; +} + +SDValue XtensaTargetLowering::LowerConstantPool(ConstantPoolSDNode *CP, + SelectionDAG &DAG) const { + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + + SDValue Result; + if (CP->isMachineConstantPoolEntry()) + Result = + DAG.getTargetConstantPool(CP->getMachineCPVal(), PtrVT, CP->getAlign()); + else + Result = DAG.getTargetConstantPool(CP->getConstVal(), PtrVT, CP->getAlign(), + CP->getOffset()); + + return getAddrPCRel(Result, DAG); +} + +SDValue XtensaTargetLowering::LowerSTACKSAVE(SDValue Op, + SelectionDAG &DAG) const { + unsigned sp = Xtensa::SP; + return DAG.getCopyFromReg(Op.getOperand(0), SDLoc(Op), sp, Op.getValueType()); +} + +SDValue XtensaTargetLowering::LowerSTACKRESTORE(SDValue Op, + SelectionDAG &DAG) const { + unsigned sp = Xtensa::SP; + if (Subtarget.isWinABI()) { + SDValue NewSP = + DAG.getNode(XtensaISD::MOVSP, SDLoc(Op), MVT::i32, Op.getOperand(1)); + return DAG.getCopyToReg(Op.getOperand(0), SDLoc(Op), sp, NewSP); + } else { + return DAG.getCopyToReg(Op.getOperand(0), SDLoc(Op), sp, Op.getOperand(1)); + } +} + +SDValue XtensaTargetLowering::LowerFRAMEADDR(SDValue Op, + SelectionDAG &DAG) const { + // check the depth + assert((cast(Op.getOperand(0))->getZExtValue() == 0) && + "Frame address can only be determined for current frame."); + + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo(); + MFI.setFrameAddressIsTaken(true); + EVT VT = Op.getValueType(); + SDLoc DL(Op); + + unsigned FrameReg = Subtarget.getRegisterInfo()->getFrameRegister(MF); + SDValue FrameAddr = DAG.getCopyFromReg(DAG.getEntryNode(), DL, FrameReg, VT); + return FrameAddr; +} + +SDValue XtensaTargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op, + SelectionDAG &DAG) const { + SDValue Chain = Op.getOperand(0); // Legalize the chain. + SDValue Size = Op.getOperand(1); // Legalize the size. + EVT VT = Size->getValueType(0); + SDLoc DL(Op); + + // Round up Size to 32 + SDValue Size1 = + DAG.getNode(ISD::ADD, DL, VT, Size, DAG.getConstant(31, DL, MVT::i32)); + SDValue SizeRoundUp = + DAG.getNode(ISD::AND, DL, VT, Size1, DAG.getConstant(~31, DL, MVT::i32)); + + unsigned SPReg = Xtensa::SP; + SDValue SP = DAG.getCopyFromReg(Chain, DL, SPReg, VT); + SDValue NewSP = DAG.getNode(ISD::SUB, DL, VT, SP, SizeRoundUp); // Value + if (Subtarget.isWinABI()) { + SDValue NewSP1 = DAG.getNode(XtensaISD::MOVSP, DL, MVT::i32, NewSP); + Chain = DAG.getCopyToReg(SP.getValue(1), DL, SPReg, NewSP1); // Output chain + } else { + Chain = DAG.getCopyToReg(SP.getValue(1), DL, SPReg, NewSP); // Output chain + } + + SDValue NewVal = DAG.getCopyFromReg(Chain, DL, SPReg, MVT::i32); + Chain = NewVal.getValue(1); + + SDValue Ops[2] = {NewVal, Chain}; + return DAG.getMergeValues(Ops, DL); +} + +SDValue XtensaTargetLowering::LowerVASTART(SDValue Op, + SelectionDAG &DAG) const { + MachineFunction &MF = DAG.getMachineFunction(); + XtensaFunctionInfo *XtensaFI = MF.getInfo(); + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + SDLoc DL(Op); + + SDValue Chain = Op.getOperand(0); + SDValue Addr = Op.getOperand(1); + + // typedef struct __va_list_tag { + // int32_t *__va_stk; /* Initialized to point to the position of the + // * first argument in memory offset to account for + // the + // * arguments passed in registers and to account for + // * the size of the argument registers not being + // 16-byte + // * aligned. E.G., there are 6 argument registers + // * of 4 bytes each, but we want the __va_ndx for the + // * first stack argument to have the maximal + // * alignment of 16 bytes, so we offset the __va_stk + // address by + // * 32 bytes so that __va_stk[32] references the + // first + // * argument on the stack. + // */ + // int32_t *__va_reg; /* Points to a stack-allocated region holding the + // * contents + // * of the incoming argument registers + // */ + // int32_t __va_ndx; /* Index initialized to the position of the first + // * unnamed (variable) argument. This same index is + // also + // * used to address the arguments passed in memory. + // */ + // } __va_list_tag[1]; + + SDValue ArgAR; + SDValue OverflowPtrAdvance; + SDValue StackOffsetFI = + DAG.getFrameIndex(XtensaFI->getVarArgsStackOffset(), PtrVT); + + if (XtensaFI->getVarArgsFirstGPR() < 8) { + ArgAR = + DAG.getConstant(XtensaFI->getVarArgsFirstGPR() * 4 - 8, DL, MVT::i32); + OverflowPtrAdvance = DAG.getConstant(32, DL, PtrVT); + } else { + OverflowPtrAdvance = DAG.getNode(ISD::AND, DL, PtrVT, StackOffsetFI, + DAG.getConstant(0xf, DL, PtrVT)); + OverflowPtrAdvance = DAG.getNode(ISD::ADD, DL, PtrVT, OverflowPtrAdvance, + DAG.getConstant(32, DL, PtrVT)); + ArgAR = OverflowPtrAdvance; + } + + SDValue FR = DAG.getFrameIndex(XtensaFI->getVarArgsFrameIndex(), PtrVT); + + uint64_t FrameOffset = PtrVT.getSizeInBits() / 8; + SDValue ConstFrameOffset1 = DAG.getConstant(FrameOffset, DL, PtrVT); + SDValue ConstFrameOffset2 = DAG.getConstant(FrameOffset * 2, DL, PtrVT); + + const Value *SV = cast(Op.getOperand(2))->getValue(); + + // Store first word : arguments given in stack (__va_stk) + // Advance Argument Overflow pointer down, lest it will point to start + // after register argument va_arg finished + SDValue StackOffsetFICorr = + DAG.getNode(ISD::SUB, DL, PtrVT, StackOffsetFI, OverflowPtrAdvance); + SDValue firstStore = + DAG.getStore(Chain, DL, StackOffsetFICorr, Addr, MachinePointerInfo(SV)); + + uint64_t nextOffset = FrameOffset; + SDValue nextPtr = DAG.getNode(ISD::ADD, DL, PtrVT, Addr, ConstFrameOffset1); + + // Store second word : arguments given on registers (__va_reg) + SDValue FRAdvance = + DAG.getConstant(XtensaFI->getVarArgsFirstGPR() * 4 - 8, DL, PtrVT); + SDValue FRDecr = DAG.getNode(ISD::SUB, DL, PtrVT, FR, FRAdvance); + SDValue secondStore = DAG.getStore(firstStore, DL, FRDecr, nextPtr, + MachinePointerInfo(SV, nextOffset)); + nextOffset += FrameOffset; + nextPtr = DAG.getNode(ISD::ADD, DL, PtrVT, Addr, ConstFrameOffset2); + + // Store first word : number of int regs (__va_ndx) + return DAG.getStore(secondStore, DL, ArgAR, nextPtr, + MachinePointerInfo(SV, nextOffset)); +} + +SDValue XtensaTargetLowering::LowerVACOPY(SDValue Op, SelectionDAG &DAG) const { + // We have to copy the entire va_list struct: + // 2*sizeof(int*) + sizeof(int) = 12 Byte + unsigned VAListSize = 12; + return DAG.getMemcpy(Op.getOperand(0), Op, Op.getOperand(1), Op.getOperand(2), + DAG.getConstant(VAListSize, SDLoc(Op), MVT::i32), Align(8), + false, true, false, MachinePointerInfo(), + MachinePointerInfo()); +} + +SDValue XtensaTargetLowering::LowerShiftLeftParts(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + MVT VT = MVT::i32; + + SDValue Lo = Op.getOperand(0), Hi = Op.getOperand(1); + SDValue Shamt = Op.getOperand(2); + + SDValue SetShiftLeft = DAG.getNode(XtensaISD::SSL, DL, MVT::Glue, Shamt); + SDValue ShiftLeftHi = + DAG.getNode(XtensaISD::SRC, DL, VT, Hi, Lo, SetShiftLeft); + SDValue SetShiftLeft1 = DAG.getNode(XtensaISD::SSL, DL, MVT::Glue, Shamt); + SDValue ShiftLeftLo = DAG.getNode(XtensaISD::SHL, DL, VT, Lo, SetShiftLeft1); + SDValue Cond = DAG.getNode(ISD::AND, DL, MVT::i32, Shamt, + DAG.getConstant(VT.getSizeInBits(), DL, MVT::i32)); + Lo = DAG.getNode(ISD::SELECT, DL, VT, Cond, DAG.getConstant(0, DL, VT), + ShiftLeftLo); + Hi = DAG.getNode(ISD::SELECT, DL, VT, Cond, ShiftLeftLo, ShiftLeftHi); + + SDValue Ops[2] = {Lo, Hi}; + return DAG.getMergeValues(Ops, DL); +} + +SDValue XtensaTargetLowering::LowerShiftRightParts(SDValue Op, + SelectionDAG &DAG, + bool IsSRA) const { + SDLoc DL(Op); + SDValue Lo = Op.getOperand(0), Hi = Op.getOperand(1); + SDValue Shamt = Op.getOperand(2); + MVT VT = MVT::i32; + + if (IsSRA) { + SDValue SetShiftRight1 = DAG.getNode(XtensaISD::SSR, DL, MVT::Glue, Shamt); + SDValue ShiftRightLo1 = + DAG.getNode(XtensaISD::SRC, DL, VT, Hi, Lo, SetShiftRight1); + + SDValue SetShiftRight2 = DAG.getNode(XtensaISD::SSR, DL, MVT::Glue, Shamt); + SDValue ShiftRightHi1 = + DAG.getNode(XtensaISD::SRA, DL, VT, Hi, SetShiftRight2); + + SDValue SetShiftRight3 = DAG.getNode(XtensaISD::SSR, DL, MVT::Glue, Shamt); + SDValue ShiftRightLo2 = + DAG.getNode(XtensaISD::SRA, DL, VT, Hi, SetShiftRight3); + + SDValue ShiftRightHi2 = + DAG.getNode(ISD::SRA, DL, VT, Hi, DAG.getConstant(31, DL, VT)); + + SDValue Cond = + DAG.getNode(ISD::AND, DL, MVT::i32, Shamt, + DAG.getConstant(VT.getSizeInBits(), DL, MVT::i32)); + Hi = DAG.getNode(ISD::SELECT, DL, VT, Cond, ShiftRightHi2, ShiftRightHi1); + Lo = DAG.getNode(ISD::SELECT, DL, VT, Cond, ShiftRightLo2, ShiftRightLo1); + } else { + SDValue SetShiftRight1 = DAG.getNode(XtensaISD::SSR, DL, MVT::Glue, Shamt); + SDValue ShiftRightLo1 = + DAG.getNode(XtensaISD::SRC, DL, VT, Hi, Lo, SetShiftRight1); + + SDValue SetShiftRight2 = DAG.getNode(XtensaISD::SSR, DL, MVT::Glue, Shamt); + SDValue ShiftRightHi1 = + DAG.getNode(XtensaISD::SRL, DL, VT, Hi, SetShiftRight2); + + SDValue SetShiftRight3 = DAG.getNode(XtensaISD::SSR, DL, MVT::Glue, Shamt); + SDValue ShiftRightLo2 = + DAG.getNode(XtensaISD::SRL, DL, VT, Hi, SetShiftRight3); + + SDValue Cond = + DAG.getNode(ISD::AND, DL, MVT::i32, Shamt, + DAG.getConstant(VT.getSizeInBits(), DL, MVT::i32)); + Hi = DAG.getNode(ISD::SELECT, DL, VT, Cond, DAG.getConstant(0, DL, VT), + ShiftRightHi1); + Lo = DAG.getNode(ISD::SELECT, DL, VT, Cond, ShiftRightLo2, ShiftRightLo1); + } + + SDValue Ops[2] = {Lo, Hi}; + return DAG.getMergeValues(Ops, DL); +} + +SDValue XtensaTargetLowering::LowerFunnelShift(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + SDValue Op0 = Op.getOperand(0); + SDValue Op1 = Op.getOperand(1); + SDValue Shamt = Op.getOperand(2); + MVT VT = Op.getSimpleValueType(); + + bool IsFSHR = Op.getOpcode() == ISD::FSHR; + assert((VT == MVT::i32) && "Unexpected funnel shift type!"); + + SDValue SetSAR; + + if (!IsFSHR) { + SetSAR = DAG.getNode(XtensaISD::SSL, DL, + MVT::Glue, Shamt); + } else { + SetSAR = DAG.getNode(XtensaISD::SSR, DL, + MVT::Glue, Shamt); + } + + return DAG.getNode(XtensaISD::SRC, DL, VT, Op0, Op1, SetSAR); +} + +SDValue XtensaTargetLowering::LowerATOMIC_FENCE(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + SDValue Chain = Op.getOperand(0); + return DAG.getNode(XtensaISD::MEMW, DL, MVT::Other, Chain); +} + +SDValue XtensaTargetLowering::LowerOperation(SDValue Op, + SelectionDAG &DAG) const { + switch (Op.getOpcode()) { + case ISD::BR_JT: + return LowerBR_JT(Op, DAG); + case ISD::Constant: + return LowerImmediate(Op, DAG); + case ISD::ConstantFP: + return LowerImmediateFP(Op, DAG); + case ISD::RETURNADDR: + return LowerRETURNADDR(Op, DAG); + case ISD::BR_CC: + return LowerBR_CC(Op, DAG); + case ISD::SETCC: + return LowerSETCC(Op, DAG); + case ISD::SELECT_CC: + return LowerSELECT_CC(Op, DAG); + case ISD::GlobalAddress: + return LowerGlobalAddress(Op, DAG); + case ISD::GlobalTLSAddress: + return LowerGlobalTLSAddress(cast(Op), DAG); + case ISD::BlockAddress: + return LowerBlockAddress(cast(Op), DAG); + case ISD::JumpTable: + return LowerJumpTable(cast(Op), DAG); + case ISD::ConstantPool: + return LowerConstantPool(cast(Op), DAG); + case ISD::STACKSAVE: + return LowerSTACKSAVE(Op, DAG); + case ISD::STACKRESTORE: + return LowerSTACKRESTORE(Op, DAG); + case ISD::FRAMEADDR: + return LowerFRAMEADDR(Op, DAG); + case ISD::DYNAMIC_STACKALLOC: + return LowerDYNAMIC_STACKALLOC(Op, DAG); + case ISD::VASTART: + return LowerVASTART(Op, DAG); + case ISD::VACOPY: + return LowerVACOPY(Op, DAG); + case ISD::ATOMIC_FENCE: + return LowerATOMIC_FENCE(Op, DAG); + case ISD::SHL_PARTS: + return LowerShiftLeftParts(Op, DAG); + case ISD::SRA_PARTS: + return LowerShiftRightParts(Op, DAG, true); + case ISD::SRL_PARTS: + return LowerShiftRightParts(Op, DAG, false); + case ISD::FSHL: + case ISD::FSHR: + return LowerFunnelShift(Op, DAG); + default: + llvm_unreachable("Unexpected node to lower"); + } +} + +const char *XtensaTargetLowering::getTargetNodeName(unsigned Opcode) const { +#define OPCODE(NAME) \ + case XtensaISD::NAME: \ + return "XtensaISD::" #NAME + switch (Opcode) { + OPCODE(RET_FLAG); + OPCODE(RETW_FLAG); + OPCODE(CALL); + OPCODE(CALLW); + OPCODE(PCREL_WRAPPER); + OPCODE(SELECT); + OPCODE(SELECT_CC); + OPCODE(SELECT_CC_FP); + OPCODE(BR_T); + OPCODE(BR_F); + OPCODE(BR_CC_FP); + OPCODE(BR_JT); + OPCODE(CMPUO); + OPCODE(CMPUEQ); + OPCODE(CMPULE); + OPCODE(CMPULT); + OPCODE(CMPOEQ); + OPCODE(CMPOLE); + OPCODE(CMPOLT); + OPCODE(LOOPBR); + OPCODE(LOOPDEC); + OPCODE(LOOPEND); + OPCODE(MADD); + OPCODE(MSUB); + OPCODE(MOVS); + OPCODE(MEMW); + OPCODE(MOVSP); + OPCODE(RUR); + OPCODE(SHL); + OPCODE(SRA); + OPCODE(SRL); + OPCODE(SRC); + OPCODE(SSL); + OPCODE(SSR); + } + return NULL; +#undef OPCODE +} + +//===----------------------------------------------------------------------===// +// Custom insertion +//===----------------------------------------------------------------------===// + +static int GetBranchKind(int Cond, bool &BrInv) { + switch (Cond) { + case ISD::SETEQ: + case ISD::SETOEQ: + case ISD::SETUEQ: + return Xtensa::BEQ; + case ISD::SETNE: + case ISD::SETONE: + case ISD::SETUNE: + return Xtensa::BNE; + case ISD::SETLT: + case ISD::SETOLT: + return Xtensa::BLT; + case ISD::SETLE: + case ISD::SETOLE: + BrInv = true; + return Xtensa::BGE; + case ISD::SETGT: + case ISD::SETOGT: + BrInv = true; + return Xtensa::BLT; + case ISD::SETGE: + case ISD::SETOGE: + return Xtensa::BGE; + case ISD::SETULT: + return Xtensa::BLTU; + case ISD::SETULE: + BrInv = true; + return Xtensa::BGEU; + case ISD::SETUGT: + BrInv = true; + return Xtensa::BLTU; + case ISD::SETUGE: + return Xtensa::BGEU; + default: + return -1; + } +} + +static void GetFPBranchKind(int Cond, int &BrKind, int &CmpKind) { + + switch (Cond) { + default: + llvm_unreachable("Invalid condition!"); + break; + case ISD::SETUNE: + BrKind = Xtensa::BF; + CmpKind = Xtensa::OEQ_S; + break; + case ISD::SETUO: + BrKind = Xtensa::BT; + CmpKind = Xtensa::UN_S; + break; + case ISD::SETO: + BrKind = Xtensa::BF; + CmpKind = Xtensa::UN_S; + break; + case ISD::SETUEQ: + BrKind = Xtensa::BT; + CmpKind = Xtensa::UEQ_S; + break; + case ISD::SETULE: + BrKind = Xtensa::BT; + CmpKind = Xtensa::ULE_S; + break; + case ISD::SETULT: + BrKind = Xtensa::BT; + CmpKind = Xtensa::ULT_S; + break; + case ISD::SETEQ: + case ISD::SETOEQ: + BrKind = Xtensa::BT; + CmpKind = Xtensa::OEQ_S; + break; + case ISD::SETNE: + BrKind = Xtensa::BF; + CmpKind = Xtensa::OEQ_S; + break; + case ISD::SETLE: + case ISD::SETOLE: + BrKind = Xtensa::BT; + CmpKind = Xtensa::OLE_S; + break; + case ISD::SETLT: + case ISD::SETOLT: + BrKind = Xtensa::BT; + CmpKind = Xtensa::OLT_S; + break; + case ISD::SETGE: + BrKind = Xtensa::BF; + CmpKind = Xtensa::OLT_S; + break; + case ISD::SETGT: + BrKind = Xtensa::BF; + CmpKind = Xtensa::OLE_S; + break; + } +} + +MachineBasicBlock * +XtensaTargetLowering::emitSelectCC(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo &TII = *Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + MachineOperand &LHS = MI.getOperand(1); + MachineOperand &RHS = MI.getOperand(2); + MachineOperand &TrueV = MI.getOperand(3); + MachineOperand &FalseV = MI.getOperand(4); + MachineOperand &Cond = MI.getOperand(5); + + // To "insert" a SELECT_CC instruction, we actually have to insert the + // diamond control-flow pattern. The incoming instruction knows the + // destination vreg to set, the condition code register to branch on, the + // true/false values to select between, and a branch opcode to use. + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator It = ++BB->getIterator(); + + // thisMBB: + // ... + // TrueVal = ... + // cmpTY ccX, r1, r2 + // bCC copy1MBB + // fallthrough --> copy0MBB + MachineBasicBlock *thisMBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *copy0MBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *sinkMBB = F->CreateMachineBasicBlock(LLVM_BB); + + F->insert(It, copy0MBB); + F->insert(It, sinkMBB); + + // Transfer the remainder of BB and its successor edges to sinkMBB. + sinkMBB->splice(sinkMBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + sinkMBB->transferSuccessorsAndUpdatePHIs(BB); + + // Next, add the true and fallthrough blocks as its successors. + BB->addSuccessor(copy0MBB); + BB->addSuccessor(sinkMBB); + + if ((MI.getOpcode() == Xtensa::SELECT_CC_FP_FP) || + (MI.getOpcode() == Xtensa::SELECT_CC_FP_INT)) { + int BrKind = 0; + int CmpKind = 0; + unsigned b = Xtensa::B0; + + GetFPBranchKind(Cond.getImm(), BrKind, CmpKind); + BuildMI(BB, DL, TII.get(CmpKind), b) + .addReg(LHS.getReg()) + .addReg(RHS.getReg()); + BuildMI(BB, DL, TII.get(BrKind)) + .addReg(b, RegState::Kill) + .addMBB(sinkMBB); + } else { + bool BrInv = false; + int BrKind = GetBranchKind(Cond.getImm(), BrInv); + if (BrInv) { + BuildMI(BB, DL, TII.get(BrKind)) + .addReg(RHS.getReg()) + .addReg(LHS.getReg()) + .addMBB(sinkMBB); + } else { + BuildMI(BB, DL, TII.get(BrKind)) + .addReg(LHS.getReg()) + .addReg(RHS.getReg()) + .addMBB(sinkMBB); + } + } + + // copy0MBB: + // %FalseValue = ... + // # fallthrough to sinkMBB + BB = copy0MBB; + + // Update machine-CFG edges + BB->addSuccessor(sinkMBB); + + // sinkMBB: + // %Result = phi [ %FalseValue, copy0MBB ], [ %TrueValue, thisMBB ] + // ... + BB = sinkMBB; + + BuildMI(*BB, BB->begin(), DL, TII.get(Xtensa::PHI), MI.getOperand(0).getReg()) + .addReg(FalseV.getReg()) + .addMBB(copy0MBB) + .addReg(TrueV.getReg()) + .addMBB(thisMBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +// Emit instructions for atomic_cmp_swap node for 8/16 bit operands +MachineBasicBlock * +XtensaTargetLowering::emitAtomicCmpSwap(MachineInstr &MI, MachineBasicBlock *BB, + int isByteOperand) const { + const TargetInstrInfo &TII = *Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator It = ++BB->getIterator(); + + MachineBasicBlock *thisBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *BBLoop = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *BBExit = F->CreateMachineBasicBlock(LLVM_BB); + + F->insert(It, BBLoop); + F->insert(It, BBExit); + + // Transfer the remainder of BB and its successor edges to BBExit. + BBExit->splice(BBExit->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + BBExit->transferSuccessorsAndUpdatePHIs(BB); + + BB->addSuccessor(BBLoop); + + MachineOperand &Res = MI.getOperand(0); + MachineOperand &AtomValAddr = MI.getOperand(1); + MachineOperand &CmpVal = MI.getOperand(2); + MachineOperand &SwpVal = MI.getOperand(3); + + MachineFunction *MF = BB->getParent(); + MachineRegisterInfo &MRI = MF->getRegInfo(); + const TargetRegisterClass *RC = getRegClassFor(MVT::i32); + + unsigned R1 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::MOVI), R1).addImm(3); + + unsigned ByteOffs = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::AND), ByteOffs) + .addReg(R1) + .addReg(AtomValAddr.getReg()); + + unsigned AddrAlign = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SUB), AddrAlign) + .addReg(AtomValAddr.getReg()) + .addReg(ByteOffs); + + unsigned BitOffs = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SLLI), BitOffs) + .addReg(ByteOffs) + .addImm(3); + + unsigned Mask1 = MRI.createVirtualRegister(RC); + if (isByteOperand) { + BuildMI(*BB, MI, DL, TII.get(Xtensa::MOVI), Mask1).addImm(0xff); + } else { + unsigned R2 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::MOVI), R2).addImm(1); + unsigned R3 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SLLI), R3).addReg(R2).addImm(16); + BuildMI(*BB, MI, DL, TII.get(Xtensa::ADDI), Mask1).addReg(R3).addImm(-1); + } + + BuildMI(*BB, MI, DL, TII.get(Xtensa::SSL)).addReg(BitOffs); + + unsigned R2 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::MOVI), R2).addImm(-1); + + unsigned Mask2 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SLL), Mask2).addReg(Mask1); + + unsigned Mask3 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::XOR), Mask3).addReg(Mask2).addReg(R2); + + unsigned R3 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::L32I), R3).addReg(AddrAlign).addImm(0); + + unsigned R4 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::AND), R4).addReg(R3).addReg(Mask3); + + unsigned Cmp1 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SLL), Cmp1).addReg(CmpVal.getReg()); + + unsigned Swp1 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SLL), Swp1).addReg(SwpVal.getReg()); + + BB = BBLoop; + + unsigned MaskPhi = MRI.createVirtualRegister(RC); + unsigned MaskLoop = MRI.createVirtualRegister(RC); + + BuildMI(*BB, BB->begin(), DL, TII.get(Xtensa::PHI), MaskPhi) + .addReg(MaskLoop) + .addMBB(BBLoop) + .addReg(R4) + .addMBB(thisBB); + + unsigned Cmp2 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::OR), Cmp2).addReg(Cmp1).addReg(MaskPhi); + + unsigned Swp2 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::OR), Swp2).addReg(Swp1).addReg(MaskPhi); + + BuildMI(BB, DL, TII.get(Xtensa::WSR), Xtensa::SCOMPARE1).addReg(Cmp2); + + unsigned Swp3 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::S32C1I), Swp3) + .addReg(Swp2) + .addReg(AddrAlign) + .addImm(0); + + BuildMI(BB, DL, TII.get(Xtensa::AND), MaskLoop).addReg(Swp3).addReg(Mask3); + + BuildMI(BB, DL, TII.get(Xtensa::BNE)) + .addReg(MaskLoop) + .addReg(MaskPhi) + .addMBB(BBLoop); + + BB->addSuccessor(BBLoop); + BB->addSuccessor(BBExit); + + BB = BBExit; + auto St = BBExit->begin(); + + unsigned R5 = MRI.createVirtualRegister(RC); + BuildMI(*BB, St, DL, TII.get(Xtensa::SSR)).addReg(BitOffs); + + BuildMI(*BB, St, DL, TII.get(Xtensa::SRL), R5).addReg(Swp3); + + BuildMI(*BB, St, DL, TII.get(Xtensa::AND), Res.getReg()) + .addReg(R5) + .addReg(Mask1); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +// Emit instructions for atomic_swap node for 8/16 bit operands +MachineBasicBlock * +XtensaTargetLowering::emitAtomicSwap(MachineInstr &MI, MachineBasicBlock *BB, + int isByteOperand) const { + const TargetInstrInfo &TII = *Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator It = ++BB->getIterator(); + + MachineFunction *F = BB->getParent(); + MachineBasicBlock *BBLoop1 = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *BBLoop2 = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *BBLoop3 = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *BBLoop4 = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *BBExit = F->CreateMachineBasicBlock(LLVM_BB); + + F->insert(It, BBLoop1); + F->insert(It, BBLoop2); + F->insert(It, BBLoop3); + F->insert(It, BBLoop4); + F->insert(It, BBExit); + + // Transfer the remainder of BB and its successor edges to BBExit. + BBExit->splice(BBExit->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + BBExit->transferSuccessorsAndUpdatePHIs(BB); + + BB->addSuccessor(BBLoop1); + BBLoop1->addSuccessor(BBLoop2); + BBLoop2->addSuccessor(BBLoop3); + BBLoop2->addSuccessor(BBLoop4); + BBLoop3->addSuccessor(BBLoop2); + BBLoop3->addSuccessor(BBLoop4); + BBLoop4->addSuccessor(BBLoop1); + BBLoop4->addSuccessor(BBExit); + + MachineOperand &Res = MI.getOperand(0); + MachineOperand &AtomValAddr = MI.getOperand(1); + MachineOperand &SwpVal = MI.getOperand(2); + + MachineFunction *MF = BB->getParent(); + MachineRegisterInfo &MRI = MF->getRegInfo(); + const TargetRegisterClass *RC = getRegClassFor(MVT::i32); + + unsigned R1 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::MOVI), R1).addImm(3); + + unsigned ByteOffs = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::AND), ByteOffs) + .addReg(R1) + .addReg(AtomValAddr.getReg()); + + unsigned AddrAlign = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SUB), AddrAlign) + .addReg(AtomValAddr.getReg()) + .addReg(ByteOffs); + + unsigned BitOffs = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SLLI), BitOffs) + .addReg(ByteOffs) + .addImm(3); + + unsigned Mask1 = MRI.createVirtualRegister(RC); + if (isByteOperand) { + BuildMI(*BB, MI, DL, TII.get(Xtensa::MOVI), Mask1).addImm(0xff); + } else { + unsigned R2 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::MOVI), R2).addImm(1); + unsigned R3 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SLLI), R3).addReg(R2).addImm(16); + BuildMI(*BB, MI, DL, TII.get(Xtensa::ADDI), Mask1).addReg(R3).addImm(-1); + } + + BuildMI(*BB, MI, DL, TII.get(Xtensa::SSL)).addReg(BitOffs); + + unsigned R2 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::MOVI), R2).addImm(-1); + + unsigned Mask2 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SLL), Mask2).addReg(Mask1); + + unsigned Mask3 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::XOR), Mask3).addReg(Mask2).addReg(R2); + + unsigned R3 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::L32I), R3).addReg(AddrAlign).addImm(0); + + unsigned R4 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::AND), R4).addReg(R3).addReg(Mask3); + + unsigned SwpValShifted = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SLL), SwpValShifted) + .addReg(SwpVal.getReg()); + + unsigned R5 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::L32I), R5).addReg(AddrAlign).addImm(0); + + unsigned AtomVal = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::AND), AtomVal).addReg(R5).addReg(Mask2); + + unsigned AtomValPhi = MRI.createVirtualRegister(RC); + unsigned AtomValLoop = MRI.createVirtualRegister(RC); + + BuildMI(*BBLoop1, BBLoop1->begin(), DL, TII.get(Xtensa::PHI), AtomValPhi) + .addReg(AtomValLoop) + .addMBB(BBLoop4) + .addReg(AtomVal) + .addMBB(BB); + + BB = BBLoop1; + + BuildMI(BB, DL, TII.get(Xtensa::MEMW)); + + unsigned R6 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::L32I), R6).addReg(AddrAlign).addImm(0); + + unsigned R7 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::AND), R7).addReg(R6).addReg(Mask3); + + unsigned MaskPhi = MRI.createVirtualRegister(RC); + unsigned MaskLoop = MRI.createVirtualRegister(RC); + + BuildMI(*BBLoop2, BBLoop2->begin(), DL, TII.get(Xtensa::PHI), MaskPhi) + .addReg(MaskLoop) + .addMBB(BBLoop3) + .addReg(R7) + .addMBB(BBLoop1); + + BB = BBLoop2; + + unsigned Swp1 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::OR), Swp1) + .addReg(SwpValShifted) + .addReg(MaskPhi); + + unsigned AtomVal1 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::OR), AtomVal1) + .addReg(AtomValPhi) + .addReg(MaskPhi); + + BuildMI(BB, DL, TII.get(Xtensa::WSR), Xtensa::SCOMPARE1).addReg(AtomVal1); + + unsigned Swp2 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::S32C1I), Swp2) + .addReg(Swp1) + .addReg(AddrAlign) + .addImm(0); + + BuildMI(BB, DL, TII.get(Xtensa::BEQ)) + .addReg(AtomVal1) + .addReg(Swp2) + .addMBB(BBLoop4); + + BB = BBLoop3; + + BuildMI(BB, DL, TII.get(Xtensa::AND), MaskLoop).addReg(Swp2).addReg(Mask3); + + BuildMI(BB, DL, TII.get(Xtensa::BNE)) + .addReg(MaskLoop) + .addReg(MaskPhi) + .addMBB(BBLoop2); + + BB = BBLoop4; + + BuildMI(BB, DL, TII.get(Xtensa::AND), AtomValLoop).addReg(Swp2).addReg(Mask2); + + BuildMI(BB, DL, TII.get(Xtensa::BNE)) + .addReg(AtomValLoop) + .addReg(AtomValPhi) + .addMBB(BBLoop1); + + BB = BBExit; + + auto St = BB->begin(); + + unsigned R8 = MRI.createVirtualRegister(RC); + + BuildMI(*BB, St, DL, TII.get(Xtensa::SSR)).addReg(BitOffs); + BuildMI(*BB, St, DL, TII.get(Xtensa::SRL), R8).addReg(AtomValLoop); + + if (isByteOperand) { + BuildMI(*BB, St, DL, TII.get(Xtensa::SEXT), Res.getReg()) + .addReg(R8) + .addImm(7); + } else { + BuildMI(*BB, St, DL, TII.get(Xtensa::SEXT), Res.getReg()) + .addReg(R8) + .addImm(15); + } + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +// Emit instructions for atomic_swap node for 32 bit operands +MachineBasicBlock * +XtensaTargetLowering::emitAtomicSwap(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo &TII = *Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator It = ++BB->getIterator(); + + MachineFunction *F = BB->getParent(); + MachineBasicBlock *BBLoop = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *BBExit = F->CreateMachineBasicBlock(LLVM_BB); + + F->insert(It, BBLoop); + F->insert(It, BBExit); + + // Transfer the remainder of BB and its successor edges to BBExit. + BBExit->splice(BBExit->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + BBExit->transferSuccessorsAndUpdatePHIs(BB); + + BB->addSuccessor(BBLoop); + BBLoop->addSuccessor(BBLoop); + BBLoop->addSuccessor(BBExit); + + MachineOperand &Res = MI.getOperand(0); + MachineOperand &AtomValAddr = MI.getOperand(1); + MachineOperand &SwpVal = MI.getOperand(2); + + MachineFunction *MF = BB->getParent(); + MachineRegisterInfo &MRI = MF->getRegInfo(); + const TargetRegisterClass *RC = getRegClassFor(MVT::i32); + + BuildMI(*BB, MI, DL, TII.get(Xtensa::MEMW)); + + unsigned AtomVal = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::L32I), AtomVal) + .addReg(AtomValAddr.getReg()) + .addImm(0); + + unsigned AtomValLoop = MRI.createVirtualRegister(RC); + + BuildMI(*BBLoop, BBLoop->begin(), DL, TII.get(Xtensa::PHI), Res.getReg()) + .addReg(AtomValLoop) + .addMBB(BBLoop) + .addReg(AtomVal) + .addMBB(BB); + + BB = BBLoop; + + BuildMI(BB, DL, TII.get(Xtensa::WSR), Xtensa::SCOMPARE1).addReg(Res.getReg()); + + BuildMI(BB, DL, TII.get(Xtensa::S32C1I), AtomValLoop) + .addReg(SwpVal.getReg()) + .addReg(AtomValAddr.getReg()) + .addImm(0); + + BuildMI(BB, DL, TII.get(Xtensa::BNE)) + .addReg(AtomValLoop) + .addReg(Res.getReg()) + .addMBB(BBLoop); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +MachineBasicBlock *XtensaTargetLowering::emitAtomicRMW(MachineInstr &MI, + MachineBasicBlock *BB, + unsigned Opcode, + bool inv, + bool minmax) const { + const TargetInstrInfo &TII = *Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator It = ++BB->getIterator(); + + MachineBasicBlock *ThisBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *BBLoop = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *BBExit = F->CreateMachineBasicBlock(LLVM_BB); + + F->insert(It, BBLoop); + F->insert(It, BBExit); + + // Transfer the remainder of BB and its successor edges to BB2. + BBExit->splice(BBExit->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + BBExit->transferSuccessorsAndUpdatePHIs(BB); + + BB->addSuccessor(BBLoop); + + MachineOperand &Res = MI.getOperand(0); + MachineOperand &AtomicValAddr = MI.getOperand(1); + MachineOperand &Val = MI.getOperand(2); + MachineFunction *MF = BB->getParent(); + MachineRegisterInfo &MRI = MF->getRegInfo(); + const TargetRegisterClass *RC = getRegClassFor(MVT::i32); + + unsigned R1 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::L32I), R1) + .addReg(AtomicValAddr.getReg()) + .addImm(0); + + BB = BBLoop; + + unsigned AtomicValPhi = MRI.createVirtualRegister(RC); + unsigned AtomicValLoop = MRI.createVirtualRegister(RC); + + BuildMI(*BB, BB->begin(), DL, TII.get(Xtensa::PHI), AtomicValPhi) + .addReg(AtomicValLoop) + .addMBB(BBLoop) + .addReg(R1) + .addMBB(ThisBB); + + unsigned R2 = MRI.createVirtualRegister(RC); + + if (minmax) { + MachineBasicBlock *BBLoop1 = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, BBLoop1); + BB->addSuccessor(BBLoop1); + MachineBasicBlock *BBLoop2 = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, BBLoop2); + BB->addSuccessor(BBLoop2); + + BuildMI(BB, DL, TII.get(Opcode)) + .addReg(AtomicValPhi) + .addReg(Val.getReg()) + .addMBB(BBLoop1); + + unsigned R7 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::MOV_N), R7).addReg(Val.getReg()); + + BB = BBLoop1; + unsigned R8 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::MOV_N), R8).addReg(AtomicValPhi); + BB->addSuccessor(BBLoop2); + + BB = BBLoop2; + unsigned R9 = MRI.createVirtualRegister(RC); + + BuildMI(*BB, BB->begin(), DL, TII.get(Xtensa::PHI), R9) + .addReg(R7) + .addMBB(BBLoop) + .addReg(R8) + .addMBB(BBLoop1); + BuildMI(BB, DL, TII.get(Xtensa::MOV_N), R2).addReg(R9); + } else { + BuildMI(BB, DL, TII.get(Opcode), R2) + .addReg(AtomicValPhi) + .addReg(Val.getReg()); + if (inv) { + unsigned Rtmp1 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::MOVI), Rtmp1).addImm(-1); + unsigned Rtmp2 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::XOR), Rtmp2) + .addReg(R2) + .addReg(Rtmp1); + R2 = Rtmp2; + } + } + + unsigned R4 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::WSR), Xtensa::SCOMPARE1).addReg(AtomicValPhi); + BuildMI(BB, DL, TII.get(Xtensa::S32C1I), R4) + .addReg(R2) + .addReg(AtomicValAddr.getReg(), getKillRegState(AtomicValAddr.isDead())) + .addImm(0); + + BuildMI(BB, DL, TII.get(Xtensa::MOV_N), AtomicValLoop).addReg(R4); + + BuildMI(BB, DL, TII.get(Xtensa::BNE)) + .addReg(AtomicValPhi) + .addReg(R4) + .addMBB(BBLoop); + + BB->addSuccessor(BBLoop); + BB->addSuccessor(BBExit); + + BB = BBExit; + auto st = BBExit->begin(); + + BuildMI(*BB, st, DL, TII.get(Xtensa::MOV_N), Res.getReg()).addReg(R4); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + + return BB; +} + +MachineBasicBlock * +XtensaTargetLowering::emitAtomicRMW(MachineInstr &MI, MachineBasicBlock *BB, + bool isByteOperand, unsigned Opcode, + bool inv, bool minmax) const { + const TargetInstrInfo &TII = *Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator It = ++BB->getIterator(); + + MachineBasicBlock *ThisBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *BBLoop = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *BBExit = F->CreateMachineBasicBlock(LLVM_BB); + + F->insert(It, BBLoop); + F->insert(It, BBExit); + + // Transfer the remainder of BB and its successor edges to BB2. + BBExit->splice(BBExit->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + BBExit->transferSuccessorsAndUpdatePHIs(BB); + + BB->addSuccessor(BBLoop); + + MachineOperand &Res = MI.getOperand(0); + MachineOperand &AtomValAddr = MI.getOperand(1); + MachineOperand &Val = MI.getOperand(2); + + MachineFunction *MF = BB->getParent(); + MachineRegisterInfo &MRI = MF->getRegInfo(); + const TargetRegisterClass *RC = getRegClassFor(MVT::i32); + + unsigned R1 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::MOVI), R1).addImm(3); + + unsigned ByteOffs = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::AND), ByteOffs) + .addReg(R1) + .addReg(AtomValAddr.getReg()); + + unsigned AddrAlign = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SUB), AddrAlign) + .addReg(AtomValAddr.getReg()) + .addReg(ByteOffs); + + unsigned BitOffs = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SLLI), BitOffs) + .addReg(ByteOffs) + .addImm(3); + + unsigned Mask1 = MRI.createVirtualRegister(RC); + if (isByteOperand) { + BuildMI(*BB, MI, DL, TII.get(Xtensa::MOVI), Mask1).addImm(0xff); + } else { + unsigned R2 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::MOVI), R2).addImm(1); + unsigned R3 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SLLI), R3).addReg(R2).addImm(16); + BuildMI(*BB, MI, DL, TII.get(Xtensa::ADDI), Mask1).addReg(R3).addImm(-1); + } + + BuildMI(*BB, MI, DL, TII.get(Xtensa::SSL)).addReg(BitOffs); + + unsigned R2 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::MOVI), R2).addImm(-1); + + unsigned Mask2 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SLL), Mask2).addReg(Mask1); + + unsigned Mask3 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::XOR), Mask3).addReg(Mask2).addReg(R2); + + unsigned R3 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::L32I), R3).addReg(AddrAlign).addImm(0); + + unsigned Val1 = MRI.createVirtualRegister(RC); + BuildMI(*BB, MI, DL, TII.get(Xtensa::SLL), Val1).addReg(Val.getReg()); + + BB = BBLoop; + + unsigned AtomicValPhi = MRI.createVirtualRegister(RC); + unsigned AtomicValLoop = MRI.createVirtualRegister(RC); + + BuildMI(*BB, BB->begin(), DL, TII.get(Xtensa::PHI), AtomicValPhi) + .addReg(AtomicValLoop) + .addMBB(BBLoop) + .addReg(R3) + .addMBB(ThisBB); + + unsigned Swp2; + + if (minmax) { + MachineBasicBlock *BBLoop1 = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, BBLoop1); + BB->addSuccessor(BBLoop1); + MachineBasicBlock *BBLoop2 = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, BBLoop2); + BB->addSuccessor(BBLoop2); + + unsigned R1 = MRI.createVirtualRegister(RC); + unsigned R2 = MRI.createVirtualRegister(RC); + unsigned R3 = MRI.createVirtualRegister(RC); + unsigned R4 = MRI.createVirtualRegister(RC); + + unsigned R5 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::AND), R5) + .addReg(AtomicValPhi) + .addReg(Mask2); + + BuildMI(BB, DL, TII.get(Xtensa::SRL), R1).addReg(R5); + BuildMI(BB, DL, TII.get(Xtensa::SRL), R2).addReg(Val1); + + if ((Opcode == Xtensa::BLT) || (Opcode == Xtensa::BGE)) { + if (isByteOperand) { + BuildMI(BB, DL, TII.get(Xtensa::SEXT), R3).addReg(R1).addImm(7); + BuildMI(BB, DL, TII.get(Xtensa::SEXT), R4).addReg(R2).addImm(7); + } else { + BuildMI(BB, DL, TII.get(Xtensa::SEXT), R3).addReg(R1).addImm(15); + BuildMI(BB, DL, TII.get(Xtensa::SEXT), R4).addReg(R2).addImm(15); + } + } else { + R3 = R1; + R4 = R2; + } + + BuildMI(BB, DL, TII.get(Opcode)).addReg(R3).addReg(R4).addMBB(BBLoop1); + + unsigned R7 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::MOV_N), R7).addReg(Val1); + + BB = BBLoop1; + unsigned R8 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::MOV_N), R8).addReg(AtomicValPhi); + BB->addSuccessor(BBLoop2); + + BB = BBLoop2; + unsigned R9 = MRI.createVirtualRegister(RC); + + BuildMI(*BB, BB->begin(), DL, TII.get(Xtensa::PHI), R9) + .addReg(R7) + .addMBB(BBLoop) + .addReg(R8) + .addMBB(BBLoop1); + + unsigned R10 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::AND), R10) + .addReg(AtomicValPhi) + .addReg(Mask3); + + unsigned R11 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::AND), R11).addReg(R9).addReg(Mask2); + + Swp2 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::OR), Swp2).addReg(R10).addReg(R11); + } else { + unsigned R4 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::AND), R4) + .addReg(AtomicValPhi) + .addReg(Mask2); + + unsigned Res1 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Opcode), Res1).addReg(R4).addReg(Val1); + + unsigned Swp1 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::AND), Swp1).addReg(Res1).addReg(Mask2); + + unsigned R5 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::AND), R5) + .addReg(AtomicValPhi) + .addReg(Mask3); + + if (inv) { + unsigned Rtmp1 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::XOR), Rtmp1) + .addReg(AtomicValPhi) + .addReg(Mask2); + R5 = Rtmp1; + } + + Swp2 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::OR), Swp2).addReg(Swp1).addReg(R5); + } + + unsigned Swp3 = MRI.createVirtualRegister(RC); + BuildMI(BB, DL, TII.get(Xtensa::WSR), Xtensa::SCOMPARE1).addReg(AtomicValPhi); + BuildMI(BB, DL, TII.get(Xtensa::S32C1I), Swp3) + .addReg(Swp2) + .addReg(AddrAlign) + .addImm(0); + + BuildMI(BB, DL, TII.get(Xtensa::MOV_N), AtomicValLoop).addReg(Swp3); + + BuildMI(BB, DL, TII.get(Xtensa::BNE)) + .addReg(Swp3) + .addReg(AtomicValPhi) + .addMBB(BBLoop); + + BB->addSuccessor(BBLoop); + BB->addSuccessor(BBExit); + BB = BBExit; + auto St = BBExit->begin(); + + unsigned R6 = MRI.createVirtualRegister(RC); + + BuildMI(*BB, St, DL, TII.get(Xtensa::SSR)).addReg(BitOffs); + + BuildMI(*BB, St, DL, TII.get(Xtensa::SRL), R6).addReg(AtomicValLoop); + + BuildMI(*BB, St, DL, TII.get(Xtensa::AND), Res.getReg()) + .addReg(R6) + .addReg(Mask1); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + + return BB; +} + +MachineBasicBlock *XtensaTargetLowering::EmitInstrWithCustomInserter( + MachineInstr &MI, MachineBasicBlock *MBB) const { + const TargetInstrInfo &TII = *Subtarget.getInstrInfo(); + MachineFunction *MF = MBB->getParent(); + MachineRegisterInfo &MRI = MF->getRegInfo(); + DebugLoc DL = MI.getDebugLoc(); + + switch (MI.getOpcode()) { + case Xtensa::MULA_DA_LL_LDDEC_P: + case Xtensa::MULA_DA_LH_LDDEC_P: + case Xtensa::MULA_DA_HL_LDDEC_P: + case Xtensa::MULA_DA_HH_LDDEC_P: + case Xtensa::MULA_DA_LL_LDINC_P: + case Xtensa::MULA_DA_LH_LDINC_P: + case Xtensa::MULA_DA_HL_LDINC_P: + case Xtensa::MULA_DA_HH_LDINC_P: { + MachineOperand &MW = MI.getOperand(0); + MachineOperand &S = MI.getOperand(1); + MachineOperand &MX = MI.getOperand(2); + MachineOperand &T = MI.getOperand(3); + const TargetRegisterClass *RC = getRegClassFor(MVT::i32); + unsigned Reg1 = MRI.createVirtualRegister(RC); + unsigned Reg2 = MRI.createVirtualRegister(RC); + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::L32I), Reg1) + .addReg(S.getReg()) + .addImm(0); + + unsigned Opc; + switch (MI.getOpcode()) { + case Xtensa::MULA_DA_LL_LDDEC_P: + Opc = Xtensa::MULA_DA_LL_LDDEC; + break; + case Xtensa::MULA_DA_LH_LDDEC_P: + Opc = Xtensa::MULA_DA_LH_LDDEC; + break; + case Xtensa::MULA_DA_HL_LDDEC_P: + Opc = Xtensa::MULA_DA_HL_LDDEC; + break; + case Xtensa::MULA_DA_HH_LDDEC_P: + Opc = Xtensa::MULA_DA_HH_LDDEC; + break; + case Xtensa::MULA_DA_LL_LDINC_P: + Opc = Xtensa::MULA_DA_LL_LDINC; + break; + case Xtensa::MULA_DA_LH_LDINC_P: + Opc = Xtensa::MULA_DA_LH_LDINC; + break; + case Xtensa::MULA_DA_HL_LDINC_P: + Opc = Xtensa::MULA_DA_HL_LDINC; + break; + case Xtensa::MULA_DA_HH_LDINC_P: + Opc = Xtensa::MULA_DA_HH_LDINC; + break; + } + + unsigned MWVal = MW.getImm(); + assert((MWVal < 4) && "Unexpected value of mula_da*ld* first argument, it " + "must be from m0..m3"); + unsigned MXVal = MX.getImm(); + assert((MXVal < 2) && "Unexpected value of mula_da*ld* third " + "argument, it must be m0 or m1"); + + BuildMI(*MBB, MI, DL, TII.get(Opc)) + .addReg(Xtensa::M0 + MWVal, RegState::Define) + .addReg(Reg2, RegState::Define) + .addReg(Reg1) + .addReg(Xtensa::M0 + MXVal) + .addReg(T.getReg()); + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::S32I)) + .addReg(Reg2) + .addReg(S.getReg()) + .addImm(0); + + MI.eraseFromParent(); + return MBB; + } + case Xtensa::MULA_DD_LL_LDDEC_P: + case Xtensa::MULA_DD_LH_LDDEC_P: + case Xtensa::MULA_DD_HL_LDDEC_P: + case Xtensa::MULA_DD_HH_LDDEC_P: + case Xtensa::MULA_DD_LL_LDINC_P: + case Xtensa::MULA_DD_LH_LDINC_P: + case Xtensa::MULA_DD_HL_LDINC_P: + case Xtensa::MULA_DD_HH_LDINC_P: { + MachineOperand &MW = MI.getOperand(0); + MachineOperand &S = MI.getOperand(1); + MachineOperand &MX = MI.getOperand(2); + MachineOperand &MY = MI.getOperand(3); + const TargetRegisterClass *RC = getRegClassFor(MVT::i32); + unsigned Reg1 = MRI.createVirtualRegister(RC); + unsigned Reg2 = MRI.createVirtualRegister(RC); + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::L32I), Reg1) + .addReg(S.getReg()) + .addImm(0); + + unsigned Opc; + switch (MI.getOpcode()) { + case Xtensa::MULA_DD_LL_LDDEC_P: + Opc = Xtensa::MULA_DD_LL_LDDEC; + break; + case Xtensa::MULA_DD_LH_LDDEC_P: + Opc = Xtensa::MULA_DD_LH_LDDEC; + break; + case Xtensa::MULA_DD_HL_LDDEC_P: + Opc = Xtensa::MULA_DD_HL_LDDEC; + break; + case Xtensa::MULA_DD_HH_LDDEC_P: + Opc = Xtensa::MULA_DD_HH_LDDEC; + break; + case Xtensa::MULA_DD_LL_LDINC_P: + Opc = Xtensa::MULA_DD_LL_LDINC; + break; + case Xtensa::MULA_DD_LH_LDINC_P: + Opc = Xtensa::MULA_DD_LH_LDINC; + break; + case Xtensa::MULA_DD_HL_LDINC_P: + Opc = Xtensa::MULA_DD_HL_LDINC; + break; + case Xtensa::MULA_DD_HH_LDINC_P: + Opc = Xtensa::MULA_DD_HH_LDINC; + break; + } + + unsigned MWVal = MW.getImm(); + assert((MWVal < 4) && "Unexpected value of mula_dd*ld* first argument, " + "it must be from m0..m3"); + unsigned MXVal = MX.getImm(); + assert((MXVal < 2) && "Unexpected value of mula_dd*ld* third " + "argument, it must be m0 or m1"); + unsigned MYVal = MY.getImm(); + assert(((MYVal > 1) && (MYVal < 4)) && + "Unexpected value of mula_dd*ld* fourth " + "argument, it must be m2 or m3"); + + BuildMI(*MBB, MI, DL, TII.get(Opc)) + .addReg(Xtensa::M0 + MWVal, RegState::Define) + .addReg(Reg2, RegState::Define) + .addReg(Reg1) + .addReg(Xtensa::M0 + MXVal) + .addReg(Xtensa::M0 + MYVal); + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::S32I)) + .addReg(Reg2) + .addReg(S.getReg()) + .addImm(0); + + MI.eraseFromParent(); + return MBB; + } + case Xtensa::XSR_ACCLO_P: + case Xtensa::XSR_ACCHI_P: + case Xtensa::XSR_M0_P: + case Xtensa::XSR_M1_P: + case Xtensa::XSR_M2_P: + case Xtensa::XSR_M3_P: { + MachineOperand &T = MI.getOperand(0); + const TargetRegisterClass *RC = getRegClassFor(MVT::i32); + unsigned Reg1 = MRI.createVirtualRegister(RC); + unsigned Reg2 = MRI.createVirtualRegister(RC); + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::L32I), Reg1) + .addReg(T.getReg()) + .addImm(0); + + unsigned SReg; + switch (MI.getOpcode()) { + case Xtensa::XSR_ACCLO_P: + SReg = Xtensa::ACCLO; + break; + case Xtensa::XSR_ACCHI_P: + SReg = Xtensa::ACCHI; + break; + case Xtensa::XSR_M0_P: + SReg = Xtensa::M0; + break; + case Xtensa::XSR_M1_P: + SReg = Xtensa::M1; + break; + case Xtensa::XSR_M2_P: + SReg = Xtensa::M2; + break; + case Xtensa::XSR_M3_P: + SReg = Xtensa::M3; + break; + } + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::XSR)) + .addReg(Reg2, RegState::Define) + .addReg(SReg, RegState::Define) + .addReg(Reg1) + .addReg(SReg); + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::S32I)) + .addReg(Reg2) + .addReg(T.getReg()) + .addImm(0); + + MI.eraseFromParent(); + return MBB; + } + case Xtensa::WSR_ACCLO_P: + case Xtensa::WSR_ACCHI_P: + case Xtensa::WSR_M0_P: + case Xtensa::WSR_M1_P: + case Xtensa::WSR_M2_P: + case Xtensa::WSR_M3_P: { + MachineOperand &T = MI.getOperand(0); + + unsigned SReg; + switch (MI.getOpcode()) { + case Xtensa::WSR_ACCLO_P: + SReg = Xtensa::ACCLO; + break; + case Xtensa::WSR_ACCHI_P: + SReg = Xtensa::ACCHI; + break; + case Xtensa::WSR_M0_P: + SReg = Xtensa::M0; + break; + case Xtensa::WSR_M1_P: + SReg = Xtensa::M1; + break; + case Xtensa::WSR_M2_P: + SReg = Xtensa::M2; + break; + case Xtensa::WSR_M3_P: + SReg = Xtensa::M3; + break; + } + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::WSR)) + .addReg(SReg, RegState::Define) + .addReg(T.getReg()); + MI.eraseFromParent(); + return MBB; + } + case Xtensa::LDDEC_P: + case Xtensa::LDINC_P: { + MachineOperand &MW = MI.getOperand(0); + MachineOperand &S = MI.getOperand(1); + const TargetRegisterClass *RC = getRegClassFor(MVT::i32); + unsigned Reg1 = MRI.createVirtualRegister(RC); + unsigned Reg2 = MRI.createVirtualRegister(RC); + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::L32I), Reg1) + .addReg(S.getReg()) + .addImm(0); + + unsigned Opc = Xtensa::LDDEC; + + if (MI.getOpcode() == Xtensa::LDINC_P) + Opc = Xtensa::LDINC; + + BuildMI(*MBB, MI, DL, TII.get(Opc)) + .addReg(Xtensa::M0 + MW.getImm(), RegState::Define) + .addReg(Reg2, RegState::Define) + .addReg(Reg1); + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::S32I)) + .addReg(Reg2) + .addReg(S.getReg()) + .addImm(0); + + MI.eraseFromParent(); + return MBB; + } + + case Xtensa::BRCC_FP: { + MachineOperand &Cond = MI.getOperand(0); + MachineOperand &LHS = MI.getOperand(1); + MachineOperand &RHS = MI.getOperand(2); + MachineBasicBlock *TargetBB = MI.getOperand(3).getMBB(); + int BrKind = 0; + int CmpKind = 0; + unsigned RegB = Xtensa::B0; + + GetFPBranchKind(Cond.getImm(), BrKind, CmpKind); + BuildMI(*MBB, MI, DL, TII.get(CmpKind), RegB) + .addReg(LHS.getReg()) + .addReg(RHS.getReg()); + BuildMI(*MBB, MI, DL, TII.get(BrKind)) + .addReg(RegB, RegState::Kill) + .addMBB(TargetBB); + + MI.eraseFromParent(); + return MBB; + } + + case Xtensa::SELECT_CC_FP_FP: + case Xtensa::SELECT_CC_FP_INT: + case Xtensa::SELECT_CC_INT_FP: + case Xtensa::SELECT: + return emitSelectCC(MI, MBB); + + case Xtensa::SLL_P: { + MachineOperand &R = MI.getOperand(0); + MachineOperand &S = MI.getOperand(1); + MachineOperand &SA = MI.getOperand(2); + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::SSL)).addReg(SA.getReg()); + BuildMI(*MBB, MI, DL, TII.get(Xtensa::SLL), R.getReg()).addReg(S.getReg()); + MI.eraseFromParent(); + return MBB; + } + case Xtensa::SRA_P: { + MachineOperand &R = MI.getOperand(0); + MachineOperand &T = MI.getOperand(1); + MachineOperand &SA = MI.getOperand(2); + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::SSR)).addReg(SA.getReg()); + BuildMI(*MBB, MI, DL, TII.get(Xtensa::SRA), R.getReg()).addReg(T.getReg()); + MI.eraseFromParent(); + return MBB; + } + case Xtensa::SRL_P: { + MachineOperand &R = MI.getOperand(0); + MachineOperand &T = MI.getOperand(1); + MachineOperand &SA = MI.getOperand(2); + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::SSR)).addReg(SA.getReg()); + BuildMI(*MBB, MI, DL, TII.get(Xtensa::SRL), R.getReg()).addReg(T.getReg()); + MI.eraseFromParent(); + return MBB; + } + + case Xtensa::L8I_P: { + MachineOperand &R = MI.getOperand(0); + MachineOperand &Op1 = MI.getOperand(1); + MachineOperand &Op2 = MI.getOperand(2); + + const TargetRegisterClass *RC = getRegClassFor(MVT::i32); + unsigned R1 = MRI.createVirtualRegister(RC); + + const MachineMemOperand &MMO = **MI.memoperands_begin(); + if (MMO.isVolatile()) { + BuildMI(*MBB, MI, DL, TII.get(Xtensa::MEMW)); + } + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::L8UI), R1).add(Op1).add(Op2); + + if (Subtarget.hasSEXT()) { + BuildMI(*MBB, MI, DL, TII.get(Xtensa::SEXT), R.getReg()) + .addReg(R1) + .addImm(7); + } else { + unsigned R2 = MRI.createVirtualRegister(RC); + BuildMI(*MBB, MI, DL, TII.get(Xtensa::SLLI), R2).addReg(R1).addImm(24); + BuildMI(*MBB, MI, DL, TII.get(Xtensa::SRAI), R.getReg()) + .addReg(R2) + .addImm(24); + } + + MI.eraseFromParent(); + return MBB; + } + + case Xtensa::ATOMIC_CMP_SWAP_8_P: { + return emitAtomicCmpSwap(MI, MBB, 1); + } + + case Xtensa::ATOMIC_CMP_SWAP_16_P: { + return emitAtomicCmpSwap(MI, MBB, 0); + } + + case Xtensa::ATOMIC_CMP_SWAP_32_P: { + MachineOperand &R = MI.getOperand(0); + MachineOperand &Addr = MI.getOperand(1); + MachineOperand &Cmp = MI.getOperand(2); + MachineOperand &Swap = MI.getOperand(3); + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::WSR), Xtensa::SCOMPARE1) + .addReg(Cmp.getReg()); + + BuildMI(*MBB, MI, DL, TII.get(Xtensa::S32C1I), R.getReg()) + .addReg(Swap.getReg()) + .addReg(Addr.getReg()) + .addImm(0); + + MI.eraseFromParent(); + return MBB; + } + + case Xtensa::ATOMIC_SWAP_8_P: { + return emitAtomicSwap(MI, MBB, 1); + } + + case Xtensa::ATOMIC_SWAP_16_P: { + return emitAtomicSwap(MI, MBB, 0); + } + + case Xtensa::ATOMIC_SWAP_32_P: { + return emitAtomicSwap(MI, MBB); + } + + case Xtensa::ATOMIC_LOAD_ADD_8_P: + return emitAtomicRMW(MI, MBB, true, Xtensa::ADD, false, false); + case Xtensa::ATOMIC_LOAD_SUB_8_P: + return emitAtomicRMW(MI, MBB, true, Xtensa::SUB, false, false); + case Xtensa::ATOMIC_LOAD_OR_8_P: + return emitAtomicRMW(MI, MBB, true, Xtensa::OR, false, false); + case Xtensa::ATOMIC_LOAD_XOR_8_P: + return emitAtomicRMW(MI, MBB, true, Xtensa::XOR, false, false); + case Xtensa::ATOMIC_LOAD_AND_8_P: + return emitAtomicRMW(MI, MBB, true, Xtensa::AND, false, false); + case Xtensa::ATOMIC_LOAD_NAND_8_P: + return emitAtomicRMW(MI, MBB, true, Xtensa::AND, true, false); + case Xtensa::ATOMIC_LOAD_MIN_8_P: + return emitAtomicRMW(MI, MBB, true, Xtensa::BGE, false, true); + case Xtensa::ATOMIC_LOAD_MAX_8_P: + return emitAtomicRMW(MI, MBB, true, Xtensa::BLT, false, true); + case Xtensa::ATOMIC_LOAD_UMIN_8_P: + return emitAtomicRMW(MI, MBB, true, Xtensa::BGEU, false, true); + case Xtensa::ATOMIC_LOAD_UMAX_8_P: + return emitAtomicRMW(MI, MBB, true, Xtensa::BLTU, false, true); + + case Xtensa::ATOMIC_LOAD_ADD_16_P: + return emitAtomicRMW(MI, MBB, false, Xtensa::ADD, false, false); + case Xtensa::ATOMIC_LOAD_SUB_16_P: + return emitAtomicRMW(MI, MBB, false, Xtensa::SUB, false, false); + case Xtensa::ATOMIC_LOAD_OR_16_P: + return emitAtomicRMW(MI, MBB, false, Xtensa::OR, false, false); + case Xtensa::ATOMIC_LOAD_XOR_16_P: + return emitAtomicRMW(MI, MBB, false, Xtensa::XOR, false, false); + case Xtensa::ATOMIC_LOAD_AND_16_P: + return emitAtomicRMW(MI, MBB, false, Xtensa::AND, false, false); + case Xtensa::ATOMIC_LOAD_NAND_16_P: + return emitAtomicRMW(MI, MBB, false, Xtensa::AND, true, false); + case Xtensa::ATOMIC_LOAD_MIN_16_P: + return emitAtomicRMW(MI, MBB, false, Xtensa::BGE, false, true); + case Xtensa::ATOMIC_LOAD_MAX_16_P: + return emitAtomicRMW(MI, MBB, false, Xtensa::BLT, false, true); + case Xtensa::ATOMIC_LOAD_UMIN_16_P: + return emitAtomicRMW(MI, MBB, false, Xtensa::BGEU, false, true); + case Xtensa::ATOMIC_LOAD_UMAX_16_P: + return emitAtomicRMW(MI, MBB, false, Xtensa::BLTU, false, true); + + case Xtensa::ATOMIC_LOAD_ADD_32_P: + return emitAtomicRMW(MI, MBB, Xtensa::ADD, false, false); + case Xtensa::ATOMIC_LOAD_SUB_32_P: + return emitAtomicRMW(MI, MBB, Xtensa::SUB, false, false); + case Xtensa::ATOMIC_LOAD_OR_32_P: + return emitAtomicRMW(MI, MBB, Xtensa::OR, false, false); + case Xtensa::ATOMIC_LOAD_XOR_32_P: + return emitAtomicRMW(MI, MBB, Xtensa::XOR, false, false); + case Xtensa::ATOMIC_LOAD_AND_32_P: + return emitAtomicRMW(MI, MBB, Xtensa::AND, false, false); + case Xtensa::ATOMIC_LOAD_NAND_32_P: + return emitAtomicRMW(MI, MBB, Xtensa::AND, true, false); + case Xtensa::ATOMIC_LOAD_MIN_32_P: + return emitAtomicRMW(MI, MBB, Xtensa::BGE, false, true); + case Xtensa::ATOMIC_LOAD_MAX_32_P: + return emitAtomicRMW(MI, MBB, Xtensa::BLT, false, true); + case Xtensa::ATOMIC_LOAD_UMIN_32_P: + return emitAtomicRMW(MI, MBB, Xtensa::BGEU, false, true); + case Xtensa::ATOMIC_LOAD_UMAX_32_P: + return emitAtomicRMW(MI, MBB, Xtensa::BLTU, false, true); + + case Xtensa::S8I: + case Xtensa::S16I: + case Xtensa::S32I: + case Xtensa::S32I_N: + case Xtensa::SSI: + case Xtensa::SSIP: + case Xtensa::SSX: + case Xtensa::SSXP: + case Xtensa::L8UI: + case Xtensa::L16SI: + case Xtensa::L16UI: + case Xtensa::L32I: + case Xtensa::L32I_N: + case Xtensa::LSI: + case Xtensa::LSIP: + case Xtensa::LSX: + case Xtensa::LSXP: { + const MachineMemOperand &MMO = **MI.memoperands_begin(); + if (MMO.isVolatile()) { + BuildMI(*MBB, MI, DL, TII.get(Xtensa::MEMW)); + } + return MBB; + } + default: + llvm_unreachable("Unexpected instr type to insert"); + } +} diff --git a/llvm/lib/Target/Xtensa/XtensaISelLowering.h b/llvm/lib/Target/Xtensa/XtensaISelLowering.h new file mode 100644 index 0000000000000..eaa8a0776346d --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaISelLowering.h @@ -0,0 +1,250 @@ +//===- XtensaISelLowering.h - Xtensa DAG Lowering Interface -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that Xtensa uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSAISELLOWERING_H +#define LLVM_LIB_TARGET_XTENSA_XTENSAISELLOWERING_H + +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/CodeGen/TargetLowering.h" + +namespace llvm { +namespace XtensaISD { +enum { + FIRST_NUMBER = ISD::BUILTIN_OP_END, + + BR_T, + BR_F, + + //Conditional branch with FP operands + BR_CC_FP, + + BR_JT, + + // Calls a function. Operand 0 is the chain operand and operand 1 + // is the target address. The arguments start at operand 2. + // There is an optional glue operand at the end. + CALL, + // WinABI Call version + CALLW, + + // Floating point unordered compare conditions + CMPUEQ, + CMPULE, + CMPULT, + CMPUO, + // Floating point compare conditions + CMPOEQ, + CMPOLE, + CMPOLT, + + // Branch at the end of the loop, uses result of the LOOPDEC + LOOPBR, + // Decrement loop counter + LOOPDEC, + LOOPEND, + + // FP multipy-add/sub + MADD, + MSUB, + // FP move + MOVS, + + MEMW, + + MOVSP, + + // Wraps a TargetGlobalAddress that should be loaded using PC-relative + // accesses. Operand 0 is the address. + PCREL_WRAPPER, + + // Return with a flag operand. Operand 0 is the chain operand. + RET_FLAG, + // WinABI Return + RETW_FLAG, + + RUR, + + // Selects between operand 0 and operand 1. Operand 2 is the + // mask of condition-code values for which operand 0 should be + // chosen over operand 1; it has the same form as BR_CCMASK. + // Operand 3 is the flag operand. + SELECT, + SELECT_CC, + SELECT_CC_FP, + + // Shift + SHL, + SRA, + SRL, + SRC, + SSL, + SSR +}; +} + +class XtensaSubtarget; + +class XtensaTargetLowering : public TargetLowering { +public: + explicit XtensaTargetLowering(const TargetMachine &TM, + const XtensaSubtarget &STI); + + MVT getScalarShiftAmountTy(const DataLayout &, EVT LHSTy) const override { + return LHSTy.getSizeInBits() <= 32 ? MVT::i32 : MVT::i64; + } + + /// Return the register type for a given MVT + MVT getRegisterTypeForCallingConv(LLVMContext &Context, CallingConv::ID CC, + EVT VT) const override; + + EVT getSetCCResultType(const DataLayout &, LLVMContext &, + EVT VT) const override { + if (!VT.isVector()) + return MVT::i32; + return VT.changeVectorElementTypeToInteger(); + } + + bool isFMAFasterThanFMulAndFAdd(const MachineFunction &MF, + EVT VT) const override; + + /// If a physical register, this returns the register that receives the + /// exception address on entry to an EH pad. + Register + getExceptionPointerRegister(const Constant *PersonalityFn) const override; + /// If a physical register, this returns the register that receives the + /// exception typeid on entry to a landing pad. + Register + getExceptionSelectorRegister(const Constant *PersonalityFn) const override; + + bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override; + bool isFPImmLegal(const APFloat &Imm, EVT VT, + bool ForCodeSize) const override; + const char *getTargetNodeName(unsigned Opcode) const override; + + /// Returns the size of the platform's va_list object. + unsigned getVaListSizeInBits(const DataLayout &DL) const override; + + std::pair + getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, + StringRef Constraint, MVT VT) const override; + TargetLowering::ConstraintType + getConstraintType(StringRef Constraint) const override; + TargetLowering::ConstraintWeight + getSingleConstraintMatchWeight(AsmOperandInfo &info, + const char *constraint) const override; + + void LowerAsmOperandForConstraint(SDValue Op, std::string &Constraint, + std::vector &Ops, + SelectionDAG &DAG) const override; + + SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override; + + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, + bool isVarArg, + const SmallVectorImpl &Ins, + const SDLoc &DL, SelectionDAG &DAG, + SmallVectorImpl &InVals) const override; + SDValue LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const override; + + bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF, + bool isVarArg, + const SmallVectorImpl &Outs, + LLVMContext &Context) const override; + + SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, const SDLoc &DL, + SelectionDAG &DAG) const override; + + bool shouldInsertFencesForAtomic(const Instruction *I) const override { + return true; + } + + bool shouldReduceLoadWidth(SDNode *Load, ISD::LoadExtType ExtTy, + EVT NewVT) const override { + return false; + } + + MachineBasicBlock * + EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const override; + +private: + const XtensaSubtarget &Subtarget; + + SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerImmediate(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerImmediateFP(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerGlobalTLSAddress(GlobalAddressSDNode *Node, + SelectionDAG &DAG) const; + SDValue LowerBlockAddress(BlockAddressSDNode *Node, SelectionDAG &DAG) const; + SDValue LowerJumpTable(JumpTableSDNode *JT, SelectionDAG &DAG) const; + SDValue LowerConstantPool(ConstantPoolSDNode *CP, SelectionDAG &DAG) const; + + SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSETCC(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerVACOPY(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerDYNAMIC_STACKALLOC(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSTACKSAVE(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerSTACKRESTORE(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerShiftLeftParts(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerShiftRightParts(SDValue Op, SelectionDAG &DAG, bool IsSRA) const; + SDValue LowerFunnelShift(SDValue Op, SelectionDAG &DAG) const; + + SDValue LowerATOMIC_FENCE(SDValue Op, SelectionDAG &DAG) const; + + SDValue getAddrPCRel(SDValue Op, SelectionDAG &DAG) const; + + CCAssignFn *CCAssignFnForCall(CallingConv::ID CC, bool IsVarArg) const; + + // Implement EmitInstrWithCustomInserter for individual operation types. + MachineBasicBlock *emitSelectCC(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *emitAtomicSwap(MachineInstr &MI, MachineBasicBlock *BB, + int isByteOperand) const; + MachineBasicBlock *emitAtomicCmpSwap(MachineInstr &MI, MachineBasicBlock *BB, + int isByteOperand) const; + MachineBasicBlock *emitAtomicSwap(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *emitAtomicRMW(MachineInstr &MI, MachineBasicBlock *BB, + bool isByteOperand, unsigned Opcode, + bool inv, bool minmax) const; + MachineBasicBlock *emitAtomicRMW(MachineInstr &MI, MachineBasicBlock *BB, + unsigned Opcode, bool inv, + bool minmax) const; + + unsigned getInlineAsmMemConstraint(StringRef ConstraintCode) const override { + if (ConstraintCode == "R") + return InlineAsm::Constraint_R; + else if (ConstraintCode == "ZC") + return InlineAsm::Constraint_ZC; + return TargetLowering::getInlineAsmMemConstraint(ConstraintCode); + } +}; + +} // end namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_XTENSAISELLOWERING_H */ diff --git a/llvm/lib/Target/Xtensa/XtensaInstrFormats.td b/llvm/lib/Target/Xtensa/XtensaInstrFormats.td new file mode 100644 index 0000000000000..9eba00565fede --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaInstrFormats.td @@ -0,0 +1,221 @@ +//===- XtensaInstrFormats.td - Xtensa Instruction Formats --*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Base class for Xtensa 16 & 24 bit Formats +class XtensaInst pattern, + InstrItinClass itin = NoItinerary> + : Instruction { + let Namespace = "Xtensa"; + + let Size = size; + + let OutOperandList = outs; + let InOperandList = ins; + + let AsmString = asmstr; + let Pattern = pattern; + let Itinerary = itin; + +} + +// Base class for Xtensa 24 bit Format +class XtensaInst24 pattern, + InstrItinClass itin = NoItinerary> + : XtensaInst<3, outs, ins, asmstr, pattern, itin> { + field bits<24> Inst; + field bits<24> SoftFail = 0; +} + +// Base class for Xtensa 16 bit Format +class XtensaInst16 pattern, + InstrItinClass itin = NoItinerary> + : XtensaInst<2, outs, ins, asmstr, pattern, itin> { + field bits<16> Inst; + field bits<16> SoftFail = 0; + let Predicates = [HasDensity]; +} + +class RRR_Inst op0, bits<4> op1, bits<4> op2, dag outs, dag ins, + string asmstr, list pattern, InstrItinClass itin = NoItinerary> + : XtensaInst24 { + bits<4> r; + bits<4> s; + bits<4> t; + + let Inst{23-20} = op2; + let Inst{19-16} = op1; + let Inst{15-12} = r; + let Inst{11-8} = s; + let Inst{7-4} = t; + let Inst{3-0} = op0; +} + +class RRI4_Inst op0, bits<4> op1, dag outs, dag ins, + string asmstr, list pattern, InstrItinClass itin = NoItinerary> + : XtensaInst24 { + bits<4> r; + bits<4> s; + bits<4> t; + bits<4> imm4; + + let Inst{23-20} = imm4; + let Inst{19-16} = op1; + let Inst{15-12} = r; + let Inst{11-8} = s; + let Inst{7-4} = t; + let Inst{3-0} = op0; +} + +class RRI8_Inst op0, dag outs, dag ins, + string asmstr, list pattern, InstrItinClass itin = NoItinerary> + : XtensaInst24 { + bits<4> r; + bits<4> s; + bits<4> t; + bits<8> imm8; + + let Inst{23-16} = imm8; + let Inst{15-12} = r; + let Inst{11-8} = s; + let Inst{7-4} = t; + let Inst{3-0} = op0; +} + +class RI16_Inst op0, dag outs, dag ins, + string asmstr, list pattern, InstrItinClass itin = NoItinerary> + : XtensaInst24 { + bits<4> t; + bits<16> imm16; + + let Inst{23-8} = imm16; + let Inst{7-4} = t; + let Inst{3-0} = op0; +} + +class RSR_Inst op0, bits<4> op1, bits<4> op2, dag outs, dag ins, + string asmstr, list pattern, InstrItinClass itin = NoItinerary> + : XtensaInst24 { + bits<8> sr; + bits<4> t; + + let Inst{23-20} = op2; + let Inst{19-16} = op1; + let Inst{15-8} = sr; + let Inst{7-4} = t; + let Inst{3-0} = op0; +} + +class CALL_Inst op0, dag outs, dag ins, + string asmstr, list pattern, InstrItinClass itin = NoItinerary> + : XtensaInst24 { + bits<18> offset; + bits<2> n; + + let Inst{23-6} = offset; + let Inst{5-4} = n; + let Inst{3-0} = op0; +} + +class CALLX_Inst op0, bits<4> op1, bits<4> op2, dag outs, dag ins, + string asmstr, list pattern, InstrItinClass itin = NoItinerary> + : XtensaInst24 { + bits<4> r; + bits<4> s; + bits<2> m; + bits<2> n; + + let Inst{23-20} = op2; + let Inst{19-16} = op1; + let Inst{15-12} = r; + let Inst{11-8} = s; + let Inst{7-6} = m; + let Inst{5-4} = n; + let Inst{3-0} = op0; +} + +class BRI8_Inst op0, dag outs, dag ins, + string asmstr, list pattern, InstrItinClass itin = NoItinerary> + : XtensaInst24 { + bits<8> imm8; + bits<4> r; + bits<4> s; + bits<2> m; + bits<2> n; + + let Inst{23-16} = imm8; + let Inst{15-12} = r; + let Inst{11-8} = s; + let Inst{7-6} = m; + let Inst{5-4} = n; + let Inst{3-0} = op0; +} + +class BRI12_Inst op0, bits<2> n, bits<2> m, dag outs, dag ins, + string asmstr, list pattern, InstrItinClass itin = NoItinerary> + : XtensaInst24 { + bits<12> imm12; + bits<4> s; + + let Inst{23-12} = imm12; + let Inst{11-8} = s; + let Inst{7-6} = m; + let Inst{5-4} = n; + let Inst{3-0} = op0; +} + +class RRRN_Inst op0, dag outs, dag ins, + string asmstr, list pattern, InstrItinClass itin = NoItinerary> + : XtensaInst16 { + bits<4> r; + bits<4> s; + bits<4> t; + + let Inst{15-12} = r; + let Inst{11-8} = s; + let Inst{7-4} = t; + let Inst{3-0} = op0; +} + +class RI7_Inst op0, bits<1> i, dag outs, dag ins, + string asmstr, list pattern, InstrItinClass itin = NoItinerary> + : XtensaInst16 { + bits<7> imm7; + bits<4> s; + + let Inst{15-12} = imm7{3-0}; + let Inst{11-8} = s; + let Inst{7} = i; + let Inst{6-4} = imm7{6-4}; + let Inst{3-0} = op0; +} + +class RI6_Inst op0, bits<1> i, bits<1> z, dag outs, dag ins, + string asmstr, list pattern, InstrItinClass itin = NoItinerary> + : XtensaInst16 { + bits<6> imm6; + bits<4> s; + + let Inst{15-12} = imm6{3-0}; + let Inst{11-8} = s; + let Inst{7} = i; + let Inst{6} = z; + let Inst{5-4} = imm6{5-4}; + let Inst{3-0} = op0; +} + +// Pseudo instructions +class Pseudo pattern> + : XtensaInst<2, outs, ins, asmstr, pattern> { + field bits<16> Inst; + field bits<16> SoftFail = 0; + let Inst = 0x0; + let isPseudo = 1; + let isCodeGenOnly = 1; +} diff --git a/llvm/lib/Target/Xtensa/XtensaInstrInfo.cpp b/llvm/lib/Target/Xtensa/XtensaInstrInfo.cpp new file mode 100644 index 0000000000000..bc4c68eeaa5e5 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaInstrInfo.cpp @@ -0,0 +1,750 @@ +//===- XtensaInstrInfo.cpp - Xtensa Instruction Information ---------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the Xtensa implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#include "XtensaInstrInfo.h" +#include "XtensaConstantPoolValue.h" +#include "XtensaMachineFunctionInfo.h" +#include "XtensaTargetMachine.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" + +#define GET_INSTRINFO_CTOR_DTOR +#include "XtensaGenInstrInfo.inc" + +using namespace llvm; + +static inline const MachineInstrBuilder & +addFrameReference(const MachineInstrBuilder &MIB, int FI) { + MachineInstr *MI = MIB; + MachineFunction &MF = *MI->getParent()->getParent(); + MachineFrameInfo &MFFrame = MF.getFrameInfo(); + const MCInstrDesc &MCID = MI->getDesc(); + MachineMemOperand::Flags Flags = MachineMemOperand::MONone; + if (MCID.mayLoad()) + Flags |= MachineMemOperand::MOLoad; + if (MCID.mayStore()) + Flags |= MachineMemOperand::MOStore; + int64_t Offset = 0; + Align Alignment = MFFrame.getObjectAlign(FI); + + MachineMemOperand *MMO = + MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(MF, FI, Offset), + Flags, MFFrame.getObjectSize(FI), Alignment); + return MIB.addFrameIndex(FI).addImm(Offset).addMemOperand(MMO); +} + +XtensaInstrInfo::XtensaInstrInfo(XtensaSubtarget &sti) + : XtensaGenInstrInfo(Xtensa::ADJCALLSTACKDOWN, Xtensa::ADJCALLSTACKUP), + RI(sti), STI(sti) {} + +/// Adjust SP by Amount bytes. +void XtensaInstrInfo::adjustStackPtr(unsigned SP, int64_t Amount, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + DebugLoc DL = I != MBB.end() ? I->getDebugLoc() : DebugLoc(); + + if (Amount == 0) + return; + + MachineRegisterInfo &RegInfo = MBB.getParent()->getRegInfo(); + const TargetRegisterClass *RC = &Xtensa::ARRegClass; + + // create virtual reg to store immediate + unsigned Reg = RegInfo.createVirtualRegister(RC); + + if (isInt<8>(Amount)) // addi sp, sp, amount + BuildMI(MBB, I, DL, get(Xtensa::ADDI), Reg).addReg(SP).addImm(Amount); + else { // Expand immediate that doesn't fit in 12-bit. + unsigned Reg1; + loadImmediate(MBB, I, &Reg1, Amount); + BuildMI(MBB, I, DL, get(Xtensa::ADD), Reg) + .addReg(SP) + .addReg(Reg1, RegState::Kill); + } + + if (STI.isWinABI()) { + BuildMI(MBB, I, DL, get(Xtensa::MOVSP), SP).addReg(Reg, RegState::Kill); + } else { + BuildMI(MBB, I, DL, get(Xtensa::OR), SP) + .addReg(Reg, RegState::Kill) + .addReg(Reg, RegState::Kill); + } +} + +void XtensaInstrInfo::copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, MCRegister DestReg, + MCRegister SrcReg, bool KillSrc) const { + unsigned Opcode; + + // when we are copying a phys reg we want the bits for fp + if (Xtensa::ARRegClass.contains(DestReg, SrcReg)) { + BuildMI(MBB, MBBI, DL, get(Xtensa::OR), DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)) + .addReg(SrcReg, getKillRegState(KillSrc)); + return; + } else if (STI.hasSingleFloat() && Xtensa::FPRRegClass.contains(SrcReg) && + Xtensa::FPRRegClass.contains(DestReg)) + Opcode = Xtensa::MOV_S; + else if (STI.hasSingleFloat() && Xtensa::FPRRegClass.contains(SrcReg) && + Xtensa::ARRegClass.contains(DestReg)) + Opcode = Xtensa::RFR; + else if (STI.hasSingleFloat() && Xtensa::ARRegClass.contains(SrcReg) && + Xtensa::FPRRegClass.contains(DestReg)) + Opcode = Xtensa::WFR; + else + llvm_unreachable("Impossible reg-to-reg copy"); + + BuildMI(MBB, MBBI, DL, get(Opcode), DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)); +} + +void XtensaInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + Register SrcReg, bool isKill, + int FrameIdx, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const { + DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); + unsigned LoadOpcode, StoreOpcode; + getLoadStoreOpcodes(RC, LoadOpcode, StoreOpcode, FrameIdx); + addFrameReference(BuildMI(MBB, MBBI, DL, get(StoreOpcode)) + .addReg(SrcReg, getKillRegState(isKill)), + FrameIdx); +} + +void XtensaInstrInfo::loadRegFromStackSlot( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, Register DestReg, + int FrameIdx, const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const { + DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); + unsigned LoadOpcode, StoreOpcode; + getLoadStoreOpcodes(RC, LoadOpcode, StoreOpcode, FrameIdx); + addFrameReference(BuildMI(MBB, MBBI, DL, get(LoadOpcode), DestReg), FrameIdx); +} + +void XtensaInstrInfo::getLoadStoreOpcodes(const TargetRegisterClass *RC, + unsigned &LoadOpcode, + unsigned &StoreOpcode, + int64_t offset) const { + if (RC == &Xtensa::ARRegClass) { + LoadOpcode = Xtensa::L32I; + StoreOpcode = Xtensa::S32I; + } else if (RC == &Xtensa::FPRRegClass) { + LoadOpcode = Xtensa::LSI; + StoreOpcode = Xtensa::SSI; + } else + llvm_unreachable("Unsupported regclass to load or store"); +} + +void XtensaInstrInfo::loadImmediate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + unsigned *Reg, int64_t Value) const { + DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); + MachineRegisterInfo &RegInfo = MBB.getParent()->getRegInfo(); + const TargetRegisterClass *RC = &Xtensa::ARRegClass; + + // create virtual reg to store immediate + *Reg = RegInfo.createVirtualRegister(RC); + if (Value >= -2048 && Value <= 2047) { + BuildMI(MBB, MBBI, DL, get(Xtensa::MOVI), *Reg).addImm(Value); + } else if (Value >= -32768 && Value <= 32767) { + int Low = Value & 0xFF; + int High = Value & ~0xFF; + + BuildMI(MBB, MBBI, DL, get(Xtensa::MOVI), *Reg).addImm(Low); + BuildMI(MBB, MBBI, DL, get(Xtensa::ADDMI), *Reg).addReg(*Reg).addImm(High); + } else if (Value >= -4294967296LL && Value <= 4294967295LL) { + // 32 bit arbirary constant + MachineConstantPool *MCP = MBB.getParent()->getConstantPool(); + uint64_t UVal = ((uint64_t)Value) & 0xFFFFFFFFLL; + const Constant *CVal = ConstantInt::get( + Type::getInt32Ty(MBB.getParent()->getFunction().getContext()), UVal, + false); + unsigned Idx = MCP->getConstantPoolIndex(CVal, Align(2U)); + // MCSymbol MSym + BuildMI(MBB, MBBI, DL, get(Xtensa::L32R), *Reg).addConstantPoolIndex(Idx); + } else { + // use L32R to let assembler load immediate best + // TODO replace to L32R + llvm_unreachable("Unsupported load immediate value"); + } +} + +unsigned XtensaInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { + switch (MI.getOpcode()) { + case TargetOpcode::INLINEASM: { // Inline Asm: Variable size. + const MachineFunction *MF = MI.getParent()->getParent(); + const char *AsmStr = MI.getOperand(0).getSymbolName(); + return getInlineAsmLength(AsmStr, *MF->getTarget().getMCAsmInfo()); + } + default: + return MI.getDesc().getSize(); + } +} + +bool XtensaInstrInfo::reverseBranchCondition( + SmallVectorImpl &Cond) const { + assert(Cond.size() <= 4 && "Invalid branch condition!"); + + switch (Cond[0].getImm()) { + case Xtensa::BEQ: + Cond[0].setImm(Xtensa::BNE); + return false; + case Xtensa::BNE: + Cond[0].setImm(Xtensa::BEQ); + return false; + case Xtensa::BLT: + Cond[0].setImm(Xtensa::BGE); + return false; + case Xtensa::BGE: + Cond[0].setImm(Xtensa::BLT); + return false; + case Xtensa::BLTU: + Cond[0].setImm(Xtensa::BGEU); + return false; + case Xtensa::BGEU: + Cond[0].setImm(Xtensa::BLTU); + return false; + + case Xtensa::BEQI: + Cond[0].setImm(Xtensa::BNEI); + return false; + case Xtensa::BNEI: + Cond[0].setImm(Xtensa::BEQI); + return false; + case Xtensa::BGEI: + Cond[0].setImm(Xtensa::BLTI); + return false; + case Xtensa::BLTI: + Cond[0].setImm(Xtensa::BGEI); + return false; + case Xtensa::BGEUI: + Cond[0].setImm(Xtensa::BLTUI); + return false; + case Xtensa::BLTUI: + Cond[0].setImm(Xtensa::BGEUI); + return false; + + case Xtensa::BEQZ: + Cond[0].setImm(Xtensa::BNEZ); + return false; + case Xtensa::BNEZ: + Cond[0].setImm(Xtensa::BEQZ); + return false; + case Xtensa::BLTZ: + Cond[0].setImm(Xtensa::BGEZ); + return false; + case Xtensa::BGEZ: + Cond[0].setImm(Xtensa::BLTZ); + return false; + + case Xtensa::BF: + Cond[0].setImm(Xtensa::BT); + return false; + case Xtensa::BT: + Cond[0].setImm(Xtensa::BF); + return false; + + case Xtensa::LOOPEND: + case Xtensa::LOOPBR: + return true; + + default: + llvm_unreachable("Invalid branch condition!"); + } +} + +MachineBasicBlock * +XtensaInstrInfo::getBranchDestBlock(const MachineInstr &MI) const { + unsigned OpCode = MI.getOpcode(); + switch (OpCode) { + case Xtensa::BR_JT: + case Xtensa::JX: + return nullptr; + case Xtensa::J: + case Xtensa::LOOPEND: + return MI.getOperand(0).getMBB(); + case Xtensa::BEQ: + case Xtensa::BNE: + case Xtensa::BLT: + case Xtensa::BLTU: + case Xtensa::BGE: + case Xtensa::BGEU: + return MI.getOperand(2).getMBB(); + + case Xtensa::BEQI: + case Xtensa::BNEI: + case Xtensa::BLTI: + case Xtensa::BLTUI: + case Xtensa::BGEI: + case Xtensa::BGEUI: + return MI.getOperand(2).getMBB(); + + case Xtensa::BEQZ: + case Xtensa::BNEZ: + case Xtensa::BLTZ: + case Xtensa::BGEZ: + case Xtensa::LOOPBR: + return MI.getOperand(1).getMBB(); + + case Xtensa::BT: + case Xtensa::BF: + return MI.getOperand(1).getMBB(); + + default: + llvm_unreachable("Unknown branch opcode"); + } +} + +bool XtensaInstrInfo::isBranchOffsetInRange(unsigned BranchOp, + int64_t BrOffset) const { + switch (BranchOp) { + case Xtensa::J: + BrOffset -= 4; + return isIntN(18, BrOffset); + case Xtensa::JX: + return true; + case Xtensa::LOOPEND: + case Xtensa::LOOPBR: + BrOffset += 4; + assert((BrOffset <= 0) && "Wrong hardware loop"); + return true; + case Xtensa::BR_JT: + return true; + case Xtensa::BEQ: + case Xtensa::BNE: + case Xtensa::BLT: + case Xtensa::BLTU: + case Xtensa::BGE: + case Xtensa::BGEU: + case Xtensa::BEQI: + case Xtensa::BNEI: + case Xtensa::BLTI: + case Xtensa::BLTUI: + case Xtensa::BGEI: + case Xtensa::BGEUI: + BrOffset -= 4; + return isIntN(8, BrOffset); + case Xtensa::BEQZ: + case Xtensa::BNEZ: + case Xtensa::BLTZ: + case Xtensa::BGEZ: + BrOffset -= 4; + return isIntN(12, BrOffset); + case Xtensa::BT: + case Xtensa::BF: + BrOffset -= 4; + return isIntN(8, BrOffset); + default: + llvm_unreachable("Unknown branch opcode"); + } +} + +bool XtensaInstrInfo::analyzeBranch(MachineBasicBlock &MBB, + MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify = false) const { + // Most of the code and comments here are boilerplate. + + // Start from the bottom of the block and work up, examining the + // terminator instructions. + MachineBasicBlock::iterator I = MBB.end(); + while (I != MBB.begin()) { + --I; + if (I->isDebugValue()) + continue; + + // Working from the bottom, when we see a non-terminator instruction, we're + // done. + if (!isUnpredicatedTerminator(*I)) + break; + + // A terminator that isn't a branch can't easily be handled by this + // analysis. + SmallVector ThisCond; + ThisCond.push_back(MachineOperand::CreateImm(0)); + const MachineOperand *ThisTarget; + if (!isBranch(I, ThisCond, ThisTarget)) + return true; + + // Can't handle indirect branches. + if (!ThisTarget->isMBB()) + return true; + + if (ThisCond[0].getImm() == Xtensa::J) { + // Handle unconditional branches. + if (!AllowModify) { + TBB = ThisTarget->getMBB(); + continue; + } + + // If the block has any instructions after a JMP, delete them. + while (std::next(I) != MBB.end()) + std::next(I)->eraseFromParent(); + + Cond.clear(); + FBB = 0; + + // TBB is used to indicate the unconditinal destination. + TBB = ThisTarget->getMBB(); + continue; + } + + // Working from the bottom, handle the first conditional branch. + if (Cond.empty()) { + // FIXME: add X86-style branch swap + FBB = TBB; + TBB = ThisTarget->getMBB(); + Cond.push_back(MachineOperand::CreateImm(ThisCond[0].getImm())); + + // push remaining operands + for (unsigned int i = 0; i < (I->getNumExplicitOperands() - 1); i++) + Cond.push_back(I->getOperand(i)); + + continue; + } + + // Handle subsequent conditional branches. + assert(Cond.size() <= 4); + assert(TBB); + + // Only handle the case where all conditional branches branch to the same + // destination. + if (TBB != ThisTarget->getMBB()) + return true; + + // If the conditions are the same, we can leave them alone. + unsigned OldCond = Cond[0].getImm(); + if (OldCond == ThisCond[0].getImm()) + continue; + } + + return false; +} + +unsigned XtensaInstrInfo::removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved) const { + // Most of the code and comments here are boilerplate. + MachineBasicBlock::iterator I = MBB.end(); + unsigned Count = 0; + if (BytesRemoved) + *BytesRemoved = 0; + + while (I != MBB.begin()) { + --I; + SmallVector Cond; + Cond.push_back(MachineOperand::CreateImm(0)); + const MachineOperand *Target; + if (!isBranch(I, Cond, Target)) + break; + if (!Target->isMBB()) + break; + // Remove the branch. + if (BytesRemoved) + *BytesRemoved += getInstSizeInBytes(*I); + I->eraseFromParent(); + I = MBB.end(); + ++Count; + } + return Count; +} + +unsigned XtensaInstrInfo::insertBranch( + MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, + ArrayRef Cond, const DebugLoc &DL, int *BytesAdded) const { + unsigned Count = 0; + if (BytesAdded) + *BytesAdded = 0; + if (FBB) { + // Need to build two branches then + // one to branch to TBB on Cond + // and a second one immediately after to unconditionally jump to FBB + Count = InsertBranchAtInst(MBB, MBB.end(), TBB, Cond, DL, BytesAdded); + auto &MI = *BuildMI(&MBB, DL, get(Xtensa::J)).addMBB(FBB); + Count++; + if (BytesAdded) + *BytesAdded += getInstSizeInBytes(MI); + return Count; + } + // This function inserts the branch at the end of the MBB + Count += InsertBranchAtInst(MBB, MBB.end(), TBB, Cond, DL, BytesAdded); + return Count; +} + +void XtensaInstrInfo::insertIndirectBranch(MachineBasicBlock &MBB, + MachineBasicBlock &DestBB, + MachineBasicBlock &RestoreBB, + const DebugLoc &DL, + int64_t BrOffset, + RegScavenger *RS) const { + assert(RS && "RegScavenger required for long branching"); + assert(MBB.empty() && + "new block should be inserted for expanding unconditional branch"); + assert(MBB.pred_size() == 1); + + MachineFunction *MF = MBB.getParent(); + MachineRegisterInfo &MRI = MF->getRegInfo(); + MachineConstantPool *ConstantPool = MF->getConstantPool(); + + if (!isInt<32>(BrOffset)) + report_fatal_error( + "Branch offsets outside of the signed 32-bit range not supported"); + XtensaConstantPoolValue *C = + XtensaConstantPoolMBB::Create(MF->getFunction().getContext(), &DestBB, 0); + unsigned Idx = ConstantPool->getConstantPoolIndex(C, Align(4)); + + // FIXME: A virtual register must be used initially, as the register + // scavenger won't work with empty blocks (SIInstrInfo::insertIndirectBranch + // uses the same workaround). + Register ScratchReg = MRI.createVirtualRegister(&Xtensa::ARRegClass); + auto II = MBB.end(); + + MachineInstr &L32R = *BuildMI(MBB, II, DL, get(Xtensa::L32R), ScratchReg) + .addConstantPoolIndex(Idx); + BuildMI(MBB, II, DL, get(Xtensa::JX)).addReg(ScratchReg, RegState::Kill); + RS->enterBasicBlockEnd(MBB); + unsigned Scav = RS->scavengeRegisterBackwards(Xtensa::ARRegClass, + L32R.getIterator(), false, 0); + MRI.replaceRegWith(ScratchReg, Scav); + MRI.clearVirtRegs(); + RS->setRegUsed(Scav); +} + +unsigned XtensaInstrInfo::InsertConstBranchAtInst( + MachineBasicBlock &MBB, MachineInstr *I, int64_t offset, + ArrayRef Cond, DebugLoc DL, int *BytesAdded) const { + // Shouldn't be a fall through. + assert(&MBB && "InsertBranch must not be told to insert a fallthrough"); + assert(Cond.size() <= 4 && + "Xtensa branch conditions have less than four components!"); + + if (Cond.empty() || (Cond[0].getImm() == Xtensa::J)) { + // Unconditional branch + MachineInstr *MI = BuildMI(MBB, I, DL, get(Xtensa::J)).addImm(offset); + if (BytesAdded && MI) + *BytesAdded += getInstSizeInBytes(*MI); + return 1; + } + + unsigned Count = 0; + unsigned BR_C = Cond[0].getImm(); + MachineInstr *MI = nullptr; + switch (BR_C) { + case Xtensa::BEQ: + case Xtensa::BNE: + case Xtensa::BLT: + case Xtensa::BLTU: + case Xtensa::BGE: + case Xtensa::BGEU: + MI = BuildMI(MBB, I, DL, get(BR_C)) + .addImm(offset) + .addReg(Cond[1].getReg()) + .addReg(Cond[2].getReg()); + break; + case Xtensa::BEQI: + case Xtensa::BNEI: + case Xtensa::BLTI: + case Xtensa::BLTUI: + case Xtensa::BGEI: + case Xtensa::BGEUI: + MI = BuildMI(MBB, I, DL, get(BR_C)) + .addImm(offset) + .addReg(Cond[1].getReg()) + .addImm(Cond[2].getImm()); + break; + case Xtensa::BEQZ: + case Xtensa::BNEZ: + case Xtensa::BLTZ: + case Xtensa::BGEZ: + MI = BuildMI(MBB, I, DL, get(BR_C)).addImm(offset).addReg(Cond[1].getReg()); + break; + case Xtensa::BT: + case Xtensa::BF: + MI = BuildMI(MBB, I, DL, get(BR_C)).addImm(offset).addReg(Cond[1].getReg()); + break; + default: + llvm_unreachable("Invalid branch type!"); + } + if (BytesAdded && MI) + *BytesAdded += getInstSizeInBytes(*MI); + ++Count; + return Count; +} + +unsigned XtensaInstrInfo::InsertBranchAtInst(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + MachineBasicBlock *TBB, + ArrayRef Cond, + const DebugLoc &DL, + int *BytesAdded) const { + // Shouldn't be a fall through. + assert(TBB && "InsertBranch must not be told to insert a fallthrough"); + assert(Cond.size() <= 4 && + "Xtensa branch conditions have less than four components!"); + + if (Cond.empty() || (Cond[0].getImm() == Xtensa::J)) { + // Unconditional branch + MachineInstr *MI = BuildMI(MBB, I, DL, get(Xtensa::J)).addMBB(TBB); + if (BytesAdded && MI) + *BytesAdded += getInstSizeInBytes(*MI); + return 1; + } + + unsigned Count = 0; + unsigned BR_C = Cond[0].getImm(); + MachineInstr *MI = nullptr; + switch (BR_C) { + case Xtensa::BEQ: + case Xtensa::BNE: + case Xtensa::BLT: + case Xtensa::BLTU: + case Xtensa::BGE: + case Xtensa::BGEU: + MI = BuildMI(MBB, I, DL, get(BR_C)) + .addReg(Cond[1].getReg()) + .addReg(Cond[2].getReg()) + .addMBB(TBB); + break; + case Xtensa::BEQI: + case Xtensa::BNEI: + case Xtensa::BLTI: + case Xtensa::BLTUI: + case Xtensa::BGEI: + case Xtensa::BGEUI: + MI = BuildMI(MBB, I, DL, get(BR_C)) + .addReg(Cond[1].getReg()) + .addImm(Cond[2].getImm()) + .addMBB(TBB); + break; + case Xtensa::BEQZ: + case Xtensa::BNEZ: + case Xtensa::BLTZ: + case Xtensa::BGEZ: + case Xtensa::LOOPBR: + MI = BuildMI(MBB, I, DL, get(BR_C)).addReg(Cond[1].getReg()).addMBB(TBB); + break; + case Xtensa::BT: + case Xtensa::BF: + MI = BuildMI(MBB, I, DL, get(BR_C)).addReg(Cond[1].getReg()).addMBB(TBB); + break; + case Xtensa::LOOPEND: + MI = BuildMI(MBB, I, DL, get(Xtensa::LOOPEND)).addMBB(TBB); + break; + default: + llvm_unreachable("Invalid branch type!"); + } + if (BytesAdded && MI) + *BytesAdded += getInstSizeInBytes(*MI); + ++Count; + return Count; +} + +bool XtensaInstrInfo::analyzeCompare(const MachineInstr &MI, Register &SrcReg, + Register &SrcReg2, int64_t &Mask, + int64_t &Value) const { + unsigned Opc = MI.getOpcode(); + + switch (Opc) { + case Xtensa::BEQ: + case Xtensa::BNE: + case Xtensa::BLT: + case Xtensa::BLTU: + case Xtensa::BGE: + case Xtensa::BGEU: + SrcReg = MI.getOperand(0).getReg(); + SrcReg2 = MI.getOperand(1).getReg(); + Value = 0; + Mask = 0; + return true; + + case Xtensa::BEQI: + case Xtensa::BNEI: + case Xtensa::BLTI: + case Xtensa::BLTUI: + case Xtensa::BGEI: + case Xtensa::BGEUI: + SrcReg = MI.getOperand(0).getReg(); + Value = MI.getOperand(1).getImm(); + Mask = ~0; + return true; + + case Xtensa::BEQZ: + case Xtensa::BNEZ: + case Xtensa::BLTZ: + case Xtensa::BGEZ: + SrcReg = MI.getOperand(0).getReg(); + Value = 0; + Mask = ~0; + return true; + } + + return false; +} + +bool XtensaInstrInfo::isBranch(const MachineBasicBlock::iterator &MI, + SmallVectorImpl &Cond, + const MachineOperand *&Target) const { + unsigned OpCode = MI->getOpcode(); + switch (OpCode) { + case Xtensa::J: + case Xtensa::JX: + case Xtensa::BR_JT: + case Xtensa::LOOPEND: + Cond[0].setImm(OpCode); + Target = &MI->getOperand(0); + return true; + case Xtensa::BEQ: + case Xtensa::BNE: + case Xtensa::BLT: + case Xtensa::BLTU: + case Xtensa::BGE: + case Xtensa::BGEU: + Cond[0].setImm(OpCode); + Target = &MI->getOperand(2); + return true; + + case Xtensa::BEQI: + case Xtensa::BNEI: + case Xtensa::BLTI: + case Xtensa::BLTUI: + case Xtensa::BGEI: + case Xtensa::BGEUI: + Cond[0].setImm(OpCode); + Target = &MI->getOperand(2); + return true; + + case Xtensa::BEQZ: + case Xtensa::BNEZ: + case Xtensa::BLTZ: + case Xtensa::BGEZ: + case Xtensa::LOOPBR: + Cond[0].setImm(OpCode); + Target = &MI->getOperand(1); + return true; + + case Xtensa::BT: + case Xtensa::BF: + Cond[0].setImm(OpCode); + Target = &MI->getOperand(1); + return true; + + default: + assert(!MI->getDesc().isBranch() && "Unknown branch opcode"); + return false; + } +} \ No newline at end of file diff --git a/llvm/lib/Target/Xtensa/XtensaInstrInfo.h b/llvm/lib/Target/Xtensa/XtensaInstrInfo.h new file mode 100644 index 0000000000000..8b064613d8250 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaInstrInfo.h @@ -0,0 +1,111 @@ +//===-- XtensaInstrInfo.h - Xtensa Instruction Information ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the Xtensa implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSAINSTRINFO_H +#define LLVM_LIB_TARGET_XTENSA_XTENSAINSTRINFO_H + +#include "Xtensa.h" +#include "XtensaRegisterInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" + +#define GET_INSTRINFO_HEADER + +#include "XtensaGenInstrInfo.inc" + +namespace llvm { + +class XtensaTargetMachine; +class XtensaSubtarget; +class XtensaInstrInfo : public XtensaGenInstrInfo { + const XtensaRegisterInfo RI; + XtensaSubtarget &STI; + +public: + XtensaInstrInfo(XtensaSubtarget &STI); + + void adjustStackPtr(unsigned SP, int64_t Amount, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const; + unsigned getInstSizeInBytes(const MachineInstr &MI) const override; + + // Return the XtensaRegisterInfo, which this class owns. + const XtensaRegisterInfo &getRegisterInfo() const { return RI; } + + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, + bool KillSrc) const override; + void storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, Register SrcReg, + bool isKill, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override; + void loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, Register DestReg, + int FrameIdx, const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override; + + // Get the load and store opcodes for a given register class and offset. + void getLoadStoreOpcodes(const TargetRegisterClass *RC, unsigned &LoadOpcode, + unsigned &StoreOpcode, int64_t offset) const; + + // Emit code before MBBI in MI to move immediate value Value into + // physical register Reg. + void loadImmediate(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + unsigned *Reg, int64_t Value) const; + bool + reverseBranchCondition(SmallVectorImpl &Cond) const override; + MachineBasicBlock *getBranchDestBlock(const MachineInstr &MI) const override; + + bool isBranchOffsetInRange(unsigned BranchOpc, + int64_t BrOffset) const override; + bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const override; + unsigned removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved = nullptr) const override; + unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, ArrayRef Cond, + const DebugLoc &DL, + int *BytesAdded = nullptr) const override; + void insertIndirectBranch(MachineBasicBlock &MBB, + MachineBasicBlock &DestBB, + MachineBasicBlock &RestoreBB, const DebugLoc &DL, + int64_t BrOffset = 0, + RegScavenger *RS = nullptr) const override; + unsigned InsertBranchAtInst(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + MachineBasicBlock *TBB, + ArrayRef Cond, const DebugLoc &DL, + int *BytesAdded) const; + unsigned InsertConstBranchAtInst(MachineBasicBlock &MBB, MachineInstr *I, + int64_t offset, + ArrayRef Cond, DebugLoc DL, + int *BytesAdded) const; + bool analyzeCompare(const MachineInstr &MI, Register &SrcReg, + Register &SrcReg2, int64_t &CmpMask, + int64_t &CmpValue) const override; + + // Return true if MI is a conditional or unconditional branch. + // When returning true, set Cond to the mask of condition-code + // values on which the instruction will branch, and set Target + // to the operand that contains the branch target. This target + // can be a register or a basic block. + bool isBranch(const MachineBasicBlock::iterator &MI, + SmallVectorImpl &Cond, + const MachineOperand *&Target) const; +}; +} // end namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_XTENSAINSTRINFO_H */ diff --git a/llvm/lib/Target/Xtensa/XtensaInstrInfo.td b/llvm/lib/Target/Xtensa/XtensaInstrInfo.td new file mode 100644 index 0000000000000..8f60abfea0e4b --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaInstrInfo.td @@ -0,0 +1,1730 @@ +//===- XtensaInstrInfo.td - Target Description for Xtensa -*- tablegen -*--===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file describes the Xtensa instructions in TableGen format. +// +//===----------------------------------------------------------------------===// + +include "XtensaInstrFormats.td" +include "XtensaOperands.td" +include "XtensaOperators.td" + +//===----------------------------------------------------------------------===// +// Arithmetic & Logical instructions +//===----------------------------------------------------------------------===// + +class ArithLogic_RRR oper2, bits<4> oper1, string instrAsm, + SDPatternOperator opNode, bit isComm = 0> + : RRR_Inst<0x00, oper1, oper2, (outs AR:$r), (ins AR:$s, AR:$t), + instrAsm#"\t$r, $s, $t", + [(set AR:$r, (opNode AR:$s, AR:$t))]> { + let isCommutable = isComm; + let isReMaterializable = 0; +} + +def ADD : ArithLogic_RRR<0x08, 0x00, "add", add, 1>; +def SUB : ArithLogic_RRR<0x0C, 0x00, "sub", sub>; +def AND : ArithLogic_RRR<0x01, 0x00, "and", and, 1>; +def OR : ArithLogic_RRR<0x02, 0x00, "or", or, 1>; +def XOR : ArithLogic_RRR<0x03, 0x00, "xor", xor, 1>; + +class ADDX oper, string instrAsm, list pattern> + : RRR_Inst<0x00, 0x00, oper, (outs AR:$r), (ins AR:$s, AR:$t), + instrAsm#"\t$r, $s, $t", pattern>; + +def ADDX2 : ADDX<0x09, "addx2", [(set AR:$r, (add AR:$t, (shl AR:$s, (i32 1))))]>; +def ADDX4 : ADDX<0x0A, "addx4", [(set AR:$r, (add AR:$t, (shl AR:$s, (i32 2))))]>; +def ADDX8 : ADDX<0x0B, "addx8", [(set AR:$r, (add AR:$t, (shl AR:$s, (i32 3))))]>; + +class SUBX oper, string instrAsm, list pattern> + : RRR_Inst<0x00, 0x00, oper, (outs AR:$r), (ins AR:$s, AR:$t), + instrAsm#"\t$r, $s, $t", pattern>; + +def SUBX2 : SUBX<0x0D, "subx2", [(set AR:$r, (sub (shl AR:$s, (i32 1)), AR:$t))]>; +def SUBX4 : SUBX<0x0E, "subx4", [(set AR:$r, (sub (shl AR:$s, (i32 2)), AR:$t))]>; +def SUBX8 : SUBX<0x0F, "subx8", [(set AR:$r, (sub (shl AR:$s, (i32 3)), AR:$t))]>; + +def ABS : RRR_Inst<0x00, 0x00, 0x06, (outs AR:$r), (ins AR:$t), + "abs\t$r, $t", []> { + let s = 0x1; +} + +def ADDI : RRI8_Inst<0x02, (outs AR:$t), (ins AR:$s, imm8:$imm8), + "addi\t$t, $s, $imm8", + [(set AR:$t, (add AR:$s, imm8:$imm8))]> { + let r = 0x0C; +} + +def ADDMI : RRI8_Inst<0x02, (outs AR:$t), (ins AR:$s, imm8_sh8:$imm_sh8), + "addmi\t$t, $s, $imm_sh8", + [(set AR:$t, (add AR:$s, imm8_sh8:$imm_sh8))]> { + bits<16> imm_sh8; + + let r = 0x0D; + let imm8 = imm_sh8{15-8}; +} + +def NEG : RRR_Inst<0x00, 0x00, 0x06, (outs AR:$r), (ins AR:$t), + "neg\t$r, $t", + [(set AR:$r, (ineg AR:$t))]> { + let s = 0x00; +} + +//===----------------------------------------------------------------------===// +// Move instructions +//===----------------------------------------------------------------------===// +def MOVI : RRI8_Inst<0x02, (outs AR:$t), (ins imm12m:$imm), + "movi\t$t, $imm", + [(set AR:$t, imm12m:$imm)]> { + bits<12> imm; + + let imm8{7-0} = imm{7-0}; + let s{3-0} = imm{11-8}; + let r = 0xa; +} + +def MOVEQZ : RRR_Inst<0x00, 0x03, 0x08, (outs AR:$r), (ins AR:$s, AR:$t), + "moveqz\t$r, $s, $t", []>; +def MOVNEZ : RRR_Inst<0x00, 0x03, 0x09, (outs AR:$r), (ins AR:$s, AR:$t), + "movnez\t$r, $s, $t", []>; +def MOVLTZ : RRR_Inst<0x00, 0x03, 0x0A, (outs AR:$r), (ins AR:$s, AR:$t), + "movltz\t$r, $s, $t", []>; +def MOVGEZ : RRR_Inst<0x00, 0x03, 0x0B, (outs AR:$r), (ins AR:$s, AR:$t), + "movgez\t$r, $s, $t", []>; + +//===----------------------------------------------------------------------===// +// Shift instructions +//===----------------------------------------------------------------------===// + +let Uses = [SAR] in { + def SLL : RRR_Inst<0x00, 0x01, 0x0A, (outs AR:$r), (ins AR:$s), + "sll\t$r, $s", + [(set AR:$r, (Xtensa_shl AR:$s))]> { + let t = 0x00; + } + + def SRA : RRR_Inst<0x00, 0x01, 0x0B, (outs AR:$r), (ins AR:$t), + "sra\t$r, $t", + [(set AR:$r, (Xtensa_sra AR:$t))]> { + let s = 0x00; + } + + def SRC : RRR_Inst<0x00, 0x01, 0x08, (outs AR:$r), (ins AR:$s, AR:$t), + "src\t$r, $s, $t", + [(set AR:$r, (Xtensa_src AR:$s, AR:$t))]>; + + def SRL : RRR_Inst<0x00, 0x01, 0x09, (outs AR:$r), (ins AR:$t), + "srl\t$r, $t", + [(set AR:$r, (Xtensa_srl AR:$t))]> { + let s = 0x00; + } +} + +let Defs = [SAR] in { + def SSL : RRR_Inst<0x00, 0x00, 0x04, (outs), (ins AR:$s), + "ssl\t$s", + [(Xtensa_ssl AR:$s)]> { + let r = 0x01; + let t = 0x00; + } + + def SSR : RRR_Inst<0x00, 0x00, 0x04, (outs), (ins AR:$s), + "ssr\t$s", + [(Xtensa_ssr AR:$s)]> { + let r = 0x00; + let t = 0x00; + } +} + +def EXTUI : RRR_Inst<0x00, 0x04, 0x00, (outs AR:$r), (ins AR:$t, uimm5:$imm1, imm1_16:$imm2), + "extui\t$r, $t, $imm1, $imm2", []> { + bits<5> imm1; + bits<4> imm2; + + let s = imm1{3-0}; + let Inst{16} = imm1{4}; + let Inst{23-20} = imm2; +} + +def SRAI : RRR_Inst<0x00, 0x01, 0x02, (outs AR:$r), (ins AR:$t, uimm5:$sa), + "srai\t$r, $t, $sa", + [(set AR:$r, (sra AR:$t, uimm5:$sa))]> { + bits<5> sa; + + let Inst{20} = sa{4}; + let s = sa{3-0}; +} + +def SRLI : RRR_Inst<0x00, 0x01, 0x04, (outs AR:$r), (ins AR:$t, uimm4:$sa), + "srli\t$r, $t, $sa", + [(set AR:$r, (srl AR:$t, uimm4:$sa))]> { + bits<4> sa; + + let s = sa; +} + +def SLLI : RRR_Inst<0x00, 0x01, 0x00, (outs AR:$r), (ins AR:$s, shimm1_31:$sa), + "slli\t$r, $s, $sa", + [(set AR:$r, (shl AR:$s, shimm1_31:$sa))]> { + bits<5> sa; + + let Inst{20} = sa{4}; + let t = sa{3-0}; +} + +def SSA8L : RRR_Inst<0x00, 0x00, 0x04, (outs), (ins AR:$s), + "ssa8l\t$s", []> { + let r = 0x2; + let t = 0x0; +} + +def SSAI : RRR_Inst<0x00, 0x00, 0x04, (outs), (ins uimm5:$imm), + "ssai\t$imm", []> { + bits<5> imm; + + let r = 0x04; + let s = imm{3-0}; + let t{3-1} = 0; + let t{0} = imm{4}; +} + +// Shift Pseudo instructions: +// SSL/SSR + Shift combination +let usesCustomInserter = 1 in { + def SLL_P : Pseudo<(outs AR:$r), (ins AR:$s, AR:$sa), + "# SLL_P $r, $s, $sa", + [(set AR:$r, (shl AR:$s, AR:$sa))]>; + + def SRA_P : Pseudo<(outs AR:$r), (ins AR:$t, AR:$sa), + "# SRA_P $r, $t, $sa", + [(set AR:$r, (sra AR:$t, AR:$sa))]>; + + def SRL_P : Pseudo<(outs AR:$r), (ins AR:$t, AR:$sa), + "# SRL_P $r, $t, $sa", + [(set AR:$r, (srl AR:$t, AR:$sa))]>; +} + +//===----------------------------------------------------------------------===// +// Load and store instructions +//===----------------------------------------------------------------------===// + +// Load instructions +let mayLoad = 1, usesCustomInserter = 1 in { + + class Load_RRI8 oper, string instrAsm, SDPatternOperator opNode, + ComplexPattern addrOp, Operand memOp> + : RRI8_Inst<0x02, (outs AR:$t), (ins memOp:$addr), + instrAsm#"\t$t, $addr", + [(set AR:$t, (opNode addrOp:$addr))]> { + bits<12> addr; + + let r = oper; + let imm8{7-0} = addr{11-4}; + let s{3-0} = addr{3-0}; + } +} + +def L8UI : Load_RRI8<0x00, "l8ui", zextloadi8, addr_ish1, mem8>; +def L16SI : Load_RRI8<0x09, "l16si", sextloadi16, addr_ish2, mem16>; +def L16UI : Load_RRI8<0x01, "l16ui", zextloadi16, addr_ish2, mem16>; +def L32I : Load_RRI8<0x02, "l32i", load, addr_ish4, mem32>; + +// Store instructions +let mayStore = 1, usesCustomInserter = 1 in { + class Store_II8 oper, string instrAsm, SDPatternOperator opNode, + ComplexPattern addrOp, Operand memOp> + : RRI8_Inst<0x02, (outs), (ins AR:$t, memOp:$addr), + instrAsm#"\t$t, $addr", + [(opNode AR:$t, addrOp:$addr)]> { + bits<12> addr; + + let r = oper; + let imm8{7-0} = addr{11-4}; + let s{3-0} = addr{3-0}; + } +} + +def S8I : Store_II8<0x04, "s8i", truncstorei8, addr_ish1, mem8>; +def S16I : Store_II8<0x05, "s16i", truncstorei16, addr_ish2, mem16>; +def S32I : Store_II8<0x06, "s32i", store, addr_ish4, mem32>; + +def L32R : RI16_Inst<0x01, (outs AR:$t), (ins L32Rtarget:$label), + "l32r\t$t, $label", []> { + bits<16> label; + let imm16 = label; +} + +//pcrel addr loading using L32R +def : Pat<(Xtensa_pcrel_wrapper tconstpool:$in), (L32R tconstpool:$in)>; + +// FrameIndexes are legalized when they are operands from load/store +// instructions. The same not happens for stack address copies, so an +// add op with mem ComplexPattern is used and the stack address copy +// can be matched. +// Setting of attribute mayLoad is trick to process instruction operands +// in function XtensaRegisterInfo::eliminateFI + +let isCodeGenOnly = 1, mayLoad = 1 in { + + def LEA_ADD : RRI8_Inst<0x02, (outs AR:$t), (ins mem32:$addr), + "addi\t$t, $addr", + [(set AR:$t, addr_ish4:$addr)]> { + bits<12> addr; + + let r = 0x0C; + let imm8{7-0} = addr{11-4}; + let s{3-0} = addr{3-0}; + } +} + +// Xtensa missed L8I load operation, use pseudo operation +let usesCustomInserter = 1 in +def L8I_P: Pseudo<(outs AR:$t), (ins mem8:$addr), + "!L8I_P $t, $addr", + [(set AR:$t, (sextloadi8 + addr_ish1:$addr))]>; + +//extending loads +def : Pat<(i32 (extloadi1 addr_ish1:$addr)), (L8UI addr_ish1:$addr)>; +def : Pat<(i32 (extloadi8 addr_ish1:$addr)), (L8UI addr_ish1:$addr)>; +def : Pat<(i32 (extloadi16 addr_ish2:$addr)), (L16UI addr_ish2:$addr)>; + +//===----------------------------------------------------------------------===// +// Conditional branch instructions +//===----------------------------------------------------------------------===// +let isBranch = 1, isTerminator = 1 in { + class Branch_RR oper, string instrAsm, CondCode CC> + : RRI8_Inst<0x07, (outs), + (ins AR:$s, AR:$t, brtarget:$target), + instrAsm#"\t$s, $t, $target", + [(brcc CC, AR:$s, AR:$t, bb:$target)]> { + bits<8> target; + + let r = oper; + let imm8 = target; + } + + class Branch_RI oper, string instrAsm, CondCode CC> + : RRI8_Inst<0x06, (outs), + (ins AR:$s, b4const:$imm, brtarget:$target), + instrAsm#"\t$s, $imm, $target", + [(brcc CC, AR:$s, b4const:$imm, bb:$target)]> { + bits<4> imm; + bits<8> target; + + let t = oper; + let r = imm; + let imm8 = target; + } + + class Branch_RIU oper, string instrAsm, CondCode CC> + : RRI8_Inst<0x06, (outs), + (ins AR:$s, b4constu:$imm, brtarget:$target), + instrAsm#"\t$s, $imm, $target", + [(brcc CC, AR:$s, b4constu:$imm, bb:$target)]> { + bits<4> imm; + bits<8> target; + + let t = oper; + let r = imm; + let imm8 = target; + } + + class Branch_RZ n, bits<2> m, string instrAsm, CondCode CC> + : BRI12_Inst<0x06, n, m, (outs), + (ins AR:$s, brtarget:$target), + instrAsm#"\t$s, $target", + [(brcc CC, AR:$s, (i32 0), bb:$target)]> { + bits<12> target; + + let imm12 = target; + } +} + +def BEQ : Branch_RR<0x01, "beq", SETEQ>; +def BNE : Branch_RR<0x09, "bne", SETNE>; +def BGE : Branch_RR<0x0A, "bge", SETGE>; +def BLT : Branch_RR<0x02, "blt", SETLT>; +def BGEU : Branch_RR<0x0B, "bgeu", SETUGE>; +def BLTU : Branch_RR<0x03, "bltu", SETULT>; + +def BEQI : Branch_RI<0x02, "beqi", SETEQ>; +def BNEI : Branch_RI<0x06, "bnei", SETNE>; +def BGEI : Branch_RI<0x0E, "bgei", SETGE>; +def BLTI : Branch_RI<0x0A, "blti", SETLT>; +def BGEUI : Branch_RIU<0x0F, "bgeui", SETUGE>; +def BLTUI : Branch_RIU<0x0B, "bltui", SETULT>; + +def BEQZ : Branch_RZ<0x01, 0x00, "beqz", SETEQ>; +def BNEZ : Branch_RZ<0x01, 0x01, "bnez", SETNE>; +def BGEZ : Branch_RZ<0x01, 0x03, "bgez", SETGE>; +def BLTZ : Branch_RZ<0x01, 0x02, "bltz", SETLT>; + +def BALL : RRI8_Inst<0x07, (outs), + (ins AR:$s, AR:$t, brtarget:$target), + "ball\t$s, $t, $target", []> { + bits<8> target; + + let r = 0x04; + let imm8 = target; +} + +def BANY : RRI8_Inst<0x07, (outs), + (ins AR:$s, AR:$t, brtarget:$target), + "bany\t$s, $t, $target", []> { + bits<8> target; + + let r = 0x08; + let imm8 = target; +} + +def BBC : RRI8_Inst<0x07, (outs), + (ins AR:$s, AR:$t, brtarget:$target), + "bbc\t$s, $t, $target", []> { + bits<8> target; + + let r = 0x05; + let imm8 = target; +} + +def BBS : RRI8_Inst<0x07, (outs), + (ins AR:$s, AR:$t, brtarget:$target), + "bbs\t$s, $t, $target", []> { + bits<8> target; + + let r = 0x0d; + let imm8 = target; +} + +def BNALL : RRI8_Inst<0x07, (outs), + (ins AR:$s, AR:$t, brtarget:$target), + "bnall\t$s, $t, $target", []> { + bits<8> target; + + let r = 0x0c; + let imm8 = target; +} + +def BNONE : RRI8_Inst<0x07, (outs), + (ins AR:$s, AR:$t, brtarget:$target), + "bnone\t$s, $t, $target", []> { + bits<8> target; + + let r = 0x00; + let imm8 = target; +} + +def BBCI : RRI8_Inst<0x07, (outs), + (ins AR:$s, uimm5:$imm, brtarget:$target), + "bbci\t$s, $imm, $target", []> { + bits<8> target; + bits<5> imm; + + let r{3-1} = 0x3; + let r{0} = imm{4}; + let t{3-0} = imm{3-0}; + let imm8 = target; +} + +def : InstAlias<"bbci.l\t$s, $imm, $target", (BBCI AR:$s, uimm5:$imm, brtarget:$target)>; + +def BBSI : RRI8_Inst<0x07, (outs), + (ins AR:$s, uimm5:$imm, brtarget:$target), + "bbsi\t$s, $imm, $target", []> { + bits<8> target; + bits<5> imm; + + let r{3-1} = 0x7; + let r{0} = imm{4}; + let t{3-0} = imm{3-0}; + let imm8 = target; +} + +def : InstAlias<"bbsi.l\t$s, $imm, $target", (BBSI AR:$s, uimm5:$imm, brtarget:$target)>; + +def : Pat<(brcc SETGT, AR:$s, AR:$t, bb:$target), + (BLT AR:$t, AR:$s, bb:$target)>; +def : Pat<(brcc SETUGT, AR:$s, AR:$t, bb:$target), + (BLTU AR:$t, AR:$s, bb:$target)>; +def : Pat<(brcc SETLE, AR:$s, AR:$t, bb:$target), + (BGE AR:$t, AR:$s, bb:$target)>; +def : Pat<(brcc SETULE, AR:$s, AR:$t, bb:$target), + (BGEU AR:$t, AR:$s, bb:$target)>; + +def : Pat<(brcond (i32 (seteq AR:$s, AR:$t)), bb:$target), + (BEQ AR:$s, AR:$t, bb:$target)>; +def : Pat<(brcond (i32 (setne AR:$s, AR:$t)), bb:$target), + (BNE AR:$s, AR:$t, bb:$target)>; +def : Pat<(brcond (i32 (setge AR:$s, AR:$t)), bb:$target), + (BGE AR:$s, AR:$t, bb:$target)>; +def : Pat<(brcond (i32 (setle AR:$s, AR:$t)), bb:$target), + (BLT AR:$s, AR:$t, bb:$target)>; +def : Pat<(brcond (i32 (setuge AR:$s, AR:$t)), bb:$target), + (BGEU AR:$s, AR:$t, bb:$target)>; +def : Pat<(brcond (i32 (setult AR:$s, AR:$t)), bb:$target), + (BLTU AR:$s, AR:$t, bb:$target)>; +def : Pat<(brcond (i32 (setgt AR:$s, AR:$t)), bb:$target), + (BLT AR:$t, AR:$s, bb:$target)>; +def : Pat<(brcond (i32 (setugt AR:$s, AR:$t)), bb:$target), + (BLTU AR:$t, AR:$s, bb:$target)>; +def : Pat<(brcond (i32 (setle AR:$s, AR:$t)), bb:$target), + (BGE AR:$t, AR:$s, bb:$target)>; +def : Pat<(brcond (i32 (setule AR:$s, AR:$t)), bb:$target), + (BGEU AR:$t, AR:$s, bb:$target)>; + +def : Pat<(brcond AR:$s, bb:$target), (BNEZ AR:$s, bb:$target)>; + +//===----------------------------------------------------------------------===// +// Call and jump instructions +//===----------------------------------------------------------------------===// + +let isBranch = 1, isTerminator = 1, isBarrier = 1 in { + def J : CALL_Inst<0x06, (outs), (ins jumptarget:$offset), + "j\t$offset", + [(br bb:$offset)]> { + let n = 0x0; + } + + def JX : CALLX_Inst<0x00, 0x00, 0x00, (outs), (ins AR:$s), + "jx\t$s", + [(brind AR:$s)]> { + let m = 0x2; + let n = 0x2; + let r = 0; + let isIndirectBranch = 1; + } +} + +let isCall = 1, Defs = [A0] in { + def CALL0 : CALL_Inst<0x05, (outs), (ins pcrel32call:$offset), + "call0\t$offset", []> { + let n = 0; + } + + def CALLX0 : CALLX_Inst<0x00, 0x00, 0x00, (outs), (ins AR:$s), + "callx0\t$s", []> { + let m = 0x3; + let n = 0x0; + let r = 0; + } +} + +let isReturn = 1, isTerminator = 1, + isBarrier = 1, Uses = [A0] in { + + def RET : CALLX_Inst<0x00, 0x00, 0x00, (outs), (ins), + "ret", [(Xtensa_retflag)]> { + let m = 0x2; + let n = 0x0; + let s = 0; + let r = 0; + } +} + +// Call patterns +def : Pat<(Xtensa_call (i32 tglobaladdr:$dst)), + (CALL0 tglobaladdr:$dst)>; +def : Pat<(Xtensa_call (i32 texternalsym:$dst)), + (CALL0 texternalsym:$dst)>; +def : Pat<(Xtensa_call AR:$dst), + (CALLX0 AR:$dst)>; + +let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 1, Size = 3 in { + def BR_JT: Pseudo<(outs), (ins AR:$s, i32imm:$jt), + "!br_jt_p, $s, $jt", + [(Xtensa_brjt AR:$s, tjumptable:$jt)]>; +} + +//===----------------------------------------------------------------------===// +// Mem barrier instructions +//===----------------------------------------------------------------------===// + +def MEMW : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins), + "memw", []> { + let r = 0x2; + let t = 0x0c; + let s = 0x0; + let hasSideEffects = 1; +} + +def EXTW : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins), + "extw", []> { + let r = 0x2; + let s = 0x0; + let t = 0xd; +} + +def : Pat<(Xtensa_mem_barrier), (MEMW)>; + +//===----------------------------------------------------------------------===// +// Processor control instructions +//===----------------------------------------------------------------------===// + +def DSYNC : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins), + "dsync", []> { + let r = 0x2; + let s = 0x0; + let t = 0x3; +} + +def ISYNC : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins), + "isync", []> { + let r = 0x2; + let s = 0x0; + let t = 0x0; +} + +def RSYNC : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins), + "rsync", []> { + let r = 0x2; + let s = 0x0; + let t = 0x1; +} + +def ESYNC : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins), + "esync", []> { + let r = 0x2; + let s = 0x0; + let t = 0x2; +} + +def NOP : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins), + "nop", []> { + let r = 0x02; + let s = 0x00; + let t = 0x0f; +} + +def WSR : RSR_Inst<0x00, 0x03, 0x01, (outs SR:$sr), (ins AR:$t), + "wsr\t$t, $sr", []>; + +def RSR : RSR_Inst<0x00, 0x03, 0x00, (outs AR:$t), (ins SR:$sr), + "rsr\t$t, $sr", []>; + +def XSR : RSR_Inst<0x00, 0x01, 0x06, (outs AR:$ard, SR:$srd), (ins AR:$t, SR:$sr), + "xsr\t$t, $sr", []> { + let Constraints = "$ard = $t, $srd = $sr"; +} + +//===----------------------------------------------------------------------===// +// User Registers read/write instructions +//===----------------------------------------------------------------------===// + +def WUR : RRR_Inst<0x00, 0x03, 0x0F, (outs UR:$ur), (ins AR:$t), + "wur\t$t, $ur", []> { + bits<8> ur; + + let r = ur{7-4}; + let s = ur{3-0}; +} + +def RUR : RRR_Inst<0x00, 0x03, 0x0E, (outs AR:$r), (ins UR:$ur), + "rur\t$r, $ur", [(set AR:$r, (Xtensa_rur UR:$ur))]> { + bits<8> ur; + + let s = ur{7-4}; + let t = ur{3-0}; +} + +//===----------------------------------------------------------------------===// +// External Registers read/write instructions +//===----------------------------------------------------------------------===// + +def RER : RRR_Inst<0x00, 0x00, 0x04, (outs AR:$t), (ins AR:$s), + "rer\t$t, $s", []> { + let r = 0x6; +} + +def WER : RRR_Inst<0x00, 0x00, 0x04, (outs), (ins AR:$t, AR:$s), + "wer\t$t, $s", []> { + let r = 0x7; + let hasSideEffects = 1; +} + +//===----------------------------------------------------------------------===// +// Stack allocation +//===----------------------------------------------------------------------===// + +// ADJCALLSTACKDOWN/UP implicitly use/def SP because they may be expanded into +// a stack adjustment and the codegen must know that they may modify the stack +// pointer before prolog-epilog rewriting occurs. +let Defs = [SP], Uses = [SP] in { + def ADJCALLSTACKDOWN : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), + "#ADJCALLSTACKDOWN", + [(Xtensa_callseq_start timm:$amt1, timm:$amt2)]>; + def ADJCALLSTACKUP : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), + "#ADJCALLSTACKUP", + [(Xtensa_callseq_end timm:$amt1, timm:$amt2)]>; +} + +//===----------------------------------------------------------------------===// +// Generic select instruction +//===----------------------------------------------------------------------===// +let usesCustomInserter = 1 in { + def SELECT : Pseudo<(outs AR:$dst), (ins AR:$lhs, AR:$rhs, AR:$t, AR:$f, i32imm:$cond), + "!select $dst, $lhs, $rhs, $t, $f, $cond", + [(set AR:$dst, (Xtensa_select_cc AR:$lhs, AR:$rhs, AR:$t, AR:$f, imm:$cond))]>; +} + +//===----------------------------------------------------------------------===// +// Code Density instructions +//===----------------------------------------------------------------------===// + +class ArithLogic_RRRN oper0, string instrAsm, + SDPatternOperator opNode, bit isComm = 0> + : RRRN_Inst, Requires<[HasDensity]> { + let isCommutable = isComm; + let isReMaterializable = 0; +} + +def ADD_N : ArithLogic_RRRN<0x0a, "add.n", add, 1>; + +def ADDI_N : RRRN_Inst<0x0B, (outs AR:$r), (ins AR:$s, imm1n_15:$imm), + "addi.n\t$r, $s, $imm", + [(set AR:$r, (add AR:$s, imm1n_15:$imm))]>, Requires<[HasDensity]> { + bits<4> imm; + + let t = imm; +} + +def MOV_N : RRRN_Inst<0x0D, (outs AR:$t), (ins AR:$s), + "mov.n\t$t, $s", []>, Requires<[HasDensity]> { + let r = 0; +} + +def : InstAlias<"mov\t $t, $s", (OR AR:$t, AR:$s, AR:$s)>; + +def MOVI_N : RI7_Inst<0xc, 0x0, (outs AR:$s), (ins imm32n_95:$imm7), + "movi.n\t$s, $imm7", + [(set AR:$s, imm32n_95:$imm7)]>, Requires<[HasDensity]>; + +// Load instruction +let mayLoad = 1, usesCustomInserter = 1 in { + def L32I_N : RRRN_Inst<0x8, (outs AR:$t), (ins mem32n:$addr), + "l32i.n\t$t, $addr", []>, Requires<[HasDensity]> { + bits<8> addr; + + let r{3-0} = addr{7-4}; + let s{3-0} = addr{3-0}; + } +} + +// Store instruction +let mayStore = 1, usesCustomInserter = 1 in { + def S32I_N : RRRN_Inst<0x9, (outs), (ins AR:$t, mem32n:$addr), + "s32i.n\t$t, $addr", []>, Requires<[HasDensity]> { + bits<8> addr; + + let r{3-0} = addr{7-4}; + let s{3-0} = addr{3-0}; + } +} + +//Return instruction +let isReturn = 1, isTerminator = 1, + isBarrier = 1, Uses = [A0] in { + def RET_N : RRRN_Inst<0x0D, (outs), (ins), + "ret.n", [(Xtensa_retflag)]>, + Requires<[HasDensity]> { + let r = 0x0F; + let s = 0; + let t = 0; + } +} + +//===----------------------------------------------------------------------===// +// Windowed instructions +//===----------------------------------------------------------------------===// + +def ENTRY : BRI12_Inst<0x06, 0x3, 0x0, (outs), (ins AR:$s, entry_imm12:$imm), + "entry\t$s, $imm", []>, Requires<[HasWindowed]> { + bits<15> imm; + + let imm12{11-0} = imm{14-3}; + let Defs = [SP]; +} + +//Call instructions +let isCall = 1, Defs = [A0] in { + def CALL4 : CALL_Inst<0x05, (outs), (ins pcrel32call:$offset), + "call4\t$offset", []>, Requires<[HasWindowed]> { + let n = 1; + } + + def CALL8 : CALL_Inst<0x05, (outs), (ins pcrel32call:$offset), + "call8\t$offset", []>, Requires<[HasWindowed]> { + let n = 2; + } + + def CALL12 : CALL_Inst<0x05, (outs), (ins pcrel32call:$offset), + "call12\t$offset", []>, Requires<[HasWindowed]> { + let n = 3; + } + + def CALLX4 : CALLX_Inst<0x00, 0x00, 0x00, (outs), (ins AR:$s), + "callx4\t$s", []>, Requires<[HasWindowed]> { + let m = 0x3; + let n = 0x1; + let r = 0; + } + + def CALLX8 : CALLX_Inst<0x00, 0x00, 0x00, (outs), (ins AR:$s), + "callx8\t$s", []>, Requires<[HasWindowed]> { + let m = 0x3; + let n = 0x2; + let r = 0; + } + + def CALLX12 : CALLX_Inst<0x00, 0x00, 0x00, (outs), (ins AR:$s), + "callx12\t$s", []>, Requires<[HasWindowed]> { + let m = 0x3; + let n = 0x3; + let r = 0; + } +} + +//Windowed call patterns +def : Pat<(Xtensa_callw (i32 tglobaladdr:$dst)), + (CALL8 tglobaladdr:$dst)>; +def : Pat<(Xtensa_callw (i32 texternalsym:$dst)), + (CALL8 texternalsym:$dst)>; +def : Pat<(Xtensa_callw AR:$dst), + (CALLX8 AR:$dst)>; + +def MOVSP : RRR_Inst<0x00, 0x00, 0x00, (outs AR:$t), (ins AR:$s), + "movsp\t$t, $s", + [(set AR:$t, (Xtensa_movsp AR:$s))]>, + Requires<[HasWindowed]> { + let r = 0x01; +} + +//Return instructions +let isReturn = 1, isTerminator = 1, + isBarrier = 1, Uses = [A0] in { + def RETW_N : RRRN_Inst<0x0D, (outs), (ins), + "retw.n", [(Xtensa_retWflag)]>, + Requires<[HasWindowed, HasDensity]> { + let r = 0x0F; + let s = 0; + let t = 1; + } + + def RETW : CALLX_Inst<0x00, 0x00, 0x00, (outs), (ins), + "retw", [(Xtensa_retWflag)]>, + Requires<[HasWindowed]> { + let m = 0x2; + let n = 0x1; + let s = 0; + let r = 0; + } +} + +//Store 32-bit for Window Exceptions +def S32E : RRI4_Inst<0x00, 0x09, (outs), (ins AR:$t, AR:$s, imm64n_4n:$imm), + "s32e\t$t, $s, $imm", []>, Requires<[HasWindowed]> { + bits<6> imm; + + let r = imm{5-2}; + let imm4 = 0x4; + let mayStore = 1; +} + +def L32E : RRI4_Inst<0x00, 0x09, (outs), (ins AR:$t, AR:$s, imm64n_4n:$imm), + "l32e\t$t, $s, $imm", []>, Requires<[HasWindowed]> { + bits<6> imm; + + let r = imm{5-2}; + let imm4 = 0x0; + let mayLoad = 1; +} + +//Return from window +def RFWU : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins), + "rfwu", []>, Requires<[HasWindowed]> { + bits<4> imm; + + let r = 0x3; + let s = 0x5; + let t = 0x0; +} + +def RFWO : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins), + "rfwo", []>, Requires<[HasWindowed]> { + bits<4> imm; + + let r = 0x3; + let s = 0x4; + let t = 0x0; +} + +//Rotate window +def ROTW : RRR_Inst<0x00, 0x00, 0x04, (outs), (ins imm8n_7:$imm), + "rotw\t$imm", []>, Requires<[HasWindowed]> { + bits<4> imm; + + let r = 0x8; + let s = 0x0; + let t = imm{3-0}; +} + +//===----------------------------------------------------------------------===// +// Boolean Instructions +//===----------------------------------------------------------------------===// + +def ALL4 : RRR_Inst<0x00, 0x00, 0x00, (outs BR:$t), (ins BR:$s), + "all4\t$t, $s", []>, Requires<[HasBoolean]> { + let r = 0x9; +} + +def ALL8 : RRR_Inst<0x00, 0x00, 0x00, (outs BR:$t), (ins BR:$s), + "all8\t$t, $s", []>, Requires<[HasBoolean]> { + let r = 0xB; +} + +def ANDB : RRR_Inst<0x00, 0x02, 0x00, (outs BR:$r), (ins BR:$s, BR:$t), + "andb\t$r, $s, $t", []>, Requires<[HasBoolean]>; +def ANDBC : RRR_Inst<0x00, 0x02, 0x01, (outs BR:$r), (ins BR:$s, BR:$t), + "andbc\t$r, $s, $t", []>, Requires<[HasBoolean]>; + +def ANY4 : RRR_Inst<0x00, 0x00, 0x00, (outs BR:$t), (ins BR:$s), + "any4\t$t, $s", []>, Requires<[HasBoolean]> { + let r = 0x8; +} + +def ANY8 : RRR_Inst<0x00, 0x00, 0x00, (outs BR:$t), (ins BR:$s), + "any8\t$t, $s", []>, Requires<[HasBoolean]> { + let r = 0xA; +} + +let isBranch = 1, isTerminator = 1, Predicates = [HasBoolean] in { + def BT : RRI8_Inst<0x06, (outs), (ins BR:$b, brtarget:$target), + "bt\t$b, $target", []> { + bits<8> target; + bits<4> b; + + let r = 0x1; + let s = b; + let t = 0x7; + let imm8 = target; + } + + def BF : RRI8_Inst<0x06, (outs), (ins BR:$b, brtarget:$target), + "bf\t$b, $target", []> { + bits<8> target; + bits<4> b; + + let r = 0x0; + let s = b; + let t = 0x7; + let imm8 = target; + } +} + +def MOVF : RRR_Inst<0x00, 0x03, 0x0C, (outs AR:$r), (ins AR:$s, BR:$t), + "movf\t$r, $s, $t", []>, Requires<[HasBoolean]>; +def MOVT : RRR_Inst<0x00, 0x03, 0x0D, (outs AR:$r), (ins AR:$s, BR:$t), + "movt\t$r, $s, $t", []>, Requires<[HasBoolean]>; + +def ORB : RRR_Inst<0x00, 0x02, 0x02, (outs BR:$r), (ins BR:$s, BR:$t), + "orb\t$r, $s, $t", []>, Requires<[HasBoolean]>; +def ORBC : RRR_Inst<0x00, 0x02, 0x03, (outs BR:$r), (ins BR:$s, BR:$t), + "orbc\t$r, $s, $t", []>, Requires<[HasBoolean]>; +def XORB : RRR_Inst<0x00, 0x02, 0x04, (outs BR:$r), (ins BR:$s, BR:$t), + "xorb\t$r, $s, $t", []>, Requires<[HasBoolean]>; + +def : Pat<(Xtensa_br_t BR:$b, bb:$target), (BT BR:$b, bb:$target)>; +def : Pat<(Xtensa_br_f BR:$b, bb:$target), (BF BR:$b, bb:$target)>; + +//===----------------------------------------------------------------------===// +// Floating-Point Instructions +//===----------------------------------------------------------------------===// + +class FPArith_RRR oper2, bits<4> oper1, string instrAsm, + SDPatternOperator opNode, bit isComm = 0> + : RRR_Inst<0x00, oper1, oper2, (outs FPR:$r), (ins FPR:$s, FPR:$t), + instrAsm#"\t$r, $s, $t", + [(set FPR:$r, (opNode FPR:$s, FPR:$t))]> { + let isCommutable = isComm; + let isReMaterializable = 0; + let Predicates = [HasSingleFloat]; +} + +def ADD_S : FPArith_RRR<0x00, 0x0A, "add.s", fadd, 1>; +def SUB_S : FPArith_RRR<0x01, 0x0A, "sub.s", fsub>; +def MUL_S : FPArith_RRR<0x02, 0x0A, "mul.s", fmul, 1>; + +// FP load instructions +let mayLoad = 1, usesCustomInserter = 1, Predicates = [HasSingleFloat] in { + def LSI : RRI8_Inst<0x03, (outs FPR:$t), (ins mem32:$addr), + "lsi\t$t, $addr", []> { + bits<12> addr; + + let r = 0x00; + let imm8{7-0} = addr{11-4}; + let s{3-0} = addr{3-0}; + } + + def LSIP : RRI8_Inst<0x03, (outs FPR:$t), (ins mem32:$addr), + "lsip\t$t, $addr", []> { + bits<12> addr; + + let r = 0x08; + let imm8{7-0} = addr{11-4}; + let s{3-0} = addr{3-0}; + } + + def LSX : RRR_Inst<0x00, 0x08, 0x00, (outs), (ins FPR:$r, AR:$s, AR:$t), + "lsx\t$r, $s, $t", []>; + + def LSXP : RRR_Inst<0x00, 0x08, 0x01, (outs), (ins FPR:$r, AR:$s, AR:$t), + "lsxp\t$r, $s, $t", []>; +} + +def : Pat<(f32 (load addr_ish4:$addr)), (f32 (LSI mem32:$addr))>; + +// FP store instructions +let mayStore = 1, usesCustomInserter = 1, Predicates = [HasSingleFloat] in { + def SSI : RRI8_Inst<0x03, (outs), (ins FPR:$t, mem32:$addr), + "ssi\t$t, $addr", []> { + bits<12> addr; + + let r = 0x04; + let imm8{7-0} = addr{11-4}; + let s{3-0} = addr{3-0}; + } + + def SSIP : RRI8_Inst<0x03, (outs), (ins FPR:$t, mem32:$addr), + "ssip\t$t, $addr", []> { + bits<12> addr; + + let r = 0x0C; + let imm8{7-0} = addr{11-4}; + let s{3-0} = addr{3-0}; + } + + def SSX: RRR_Inst<0x00, 0x08, 0x04, (outs), (ins FPR:$r, AR:$s, AR:$t), + "ssx\t$r, $s, $t", []>; + + def SSXP: RRR_Inst<0x00, 0x08, 0x05, (outs), (ins FPR:$r, AR:$s, AR:$t), + "ssxp\t$r, $s, $t", []>; +} + +def : Pat<(store FPR:$t, addr_ish4:$addr), (SSI FPR:$t, mem32:$addr)>; + +// FP compare instructions +let isCompare = 1, Predicates = [HasSingleFloat] in { + class FCompare oper2, bits<4> oper1, string instrAsm, + SDPatternOperator opNode, bit isComm = 0> + : RRR_Inst<0x00, oper1, oper2, (outs BR:$b), (ins FPR:$s, FPR:$t), + instrAsm#"\t$b, $s, $t", + [(set BR:$b, (opNode FPR:$s, FPR:$t))]> { + let isCommutable = isComm; + let isReMaterializable = 0; + let Predicates = [HasSingleFloat]; + } +} + +def OEQ_S : FCompare<0x02, 0x0b, "oeq.s", Xtensa_cmpoeq, 1>; +def OLT_S : FCompare<0x04, 0x0b, "olt.s", Xtensa_cmpolt, 0>; +def OLE_S : FCompare<0x06, 0x0b, "ole.s", Xtensa_cmpole, 0>; + +def UEQ_S : FCompare<0x03, 0x0b, "ueq.s", Xtensa_cmpueq, 1>; +def ULT_S : FCompare<0x05, 0x0b, "ult.s", Xtensa_cmpult, 0>; +def ULE_S : FCompare<0x07, 0x0b, "ule.s", Xtensa_cmpule, 0>; +def UN_S : FCompare<0x01, 0x0b, "un.s", Xtensa_cmpuo, 1>; + +def ABS_S : RRR_Inst<0x00, 0x0A, 0x0F, (outs FPR:$r), (ins FPR:$s), + "abs.s\t$r, $s", + [(set FPR:$r, (fabs FPR:$s))]>, Requires<[HasSingleFloat]> { + let t = 0x01; +} + +def : Pat<(fabs FPR:$s), (ABS_S $s)>; + +def ADDEXP_S : RRR_Inst<0x00, 0x0A, 0x0F, (outs FPR:$r), (ins FPR:$s), + "addexp.s\t$r, $s", []>, Requires<[HasSingleFloat]> { + let t = 0x0E; +} + +def ADDEXPM_S : RRR_Inst<0x00, 0x0A, 0x0F, (outs FPR:$r), (ins FPR:$s), + "addexpm.s\t$r, $s", []>, Requires<[HasSingleFloat]> { + let t = 0x0F; +} + +def CEIL_S : RRR_Inst<0x00, 0x0A, 0x0B, (outs AR:$r), (ins FPR:$s, uimm4:$imm), + "ceil.s\t$r, $s, $imm", []>, Requires<[HasSingleFloat]> { + bits<4> imm; + + let t = imm; +} + +def CONST_S : RRR_Inst<0x00, 0x0a, 0x0f, (outs FPR:$r), (ins uimm4:$imm), + "const.s\t$r, $imm", []>, Requires<[HasSingleFloat]> { + bits<4> imm; + + let t = 0x03; + let s = imm{3-0}; +} + +def DIV0_S : RRR_Inst<0x00, 0x0A, 0x0F, (outs FPR:$r), (ins FPR:$s), + "div0.s\t$r, $s", []>, Requires<[HasSingleFloat]> { + let t = 0x7; +} + +def DIVN_S : RRR_Inst<0x00, 0x0A, 0x07, (outs FPR:$r), (ins FPR:$s, FPR:$t), + "divn.s\t$r, $s, $t", []>, Requires<[HasSingleFloat]>; + +def FLOAT_S : RRR_Inst<0x00, 0x0A, 0x0c, (outs FPR:$r), (ins AR:$s, uimm4:$imm), + "float.s\t$r, $s, $imm", []>, Requires<[HasSingleFloat]> { + bits<4> imm; + + let t = imm; +} + +def : Pat<(f32 (sint_to_fp AR:$s)), (FLOAT_S AR:$s, 0)>; + +def FLOOR_S : RRR_Inst<0x00, 0x0A, 0x0A, (outs AR:$r), (ins FPR:$s, uimm4:$imm), + "floor.s\t$r, $s, $imm", []>, Requires<[HasSingleFloat]> { + bits<4> imm; + + let t = imm; +} + +def MADDN_S : RRR_Inst<0x00, 0x0A, 0x06, (outs FPR:$r), (ins FPR:$s, FPR:$t), + "maddn.s\t$r, $s, $t", []>, Requires<[HasSingleFloat]> { + let isCommutable = 0; +} + +// FP multipy-add +def MADD_S : RRR_Inst<0x00, 0x0A, 0x04, (outs FPR:$r), (ins FPR:$a, FPR:$s, FPR:$t), + "madd.s\t$r, $s, $t", + [(set FPR:$r, (Xtensa_madd FPR:$a, FPR:$s, FPR:$t))]>, + Requires<[HasSingleFloat]> { + let isCommutable = 0; + let isReMaterializable = 0; + let Constraints = "$r = $a"; +} + +// fmadd: r1 * r2 + r3 +def : Pat<(fma FPR:$r1, FPR:$r2, FPR:$r3), + (MADD_S $r3, $r1, $r2)>; + +def MKDADJ_S : RRR_Inst<0x00, 0x0A, 0x0F, (outs FPR:$r), (ins FPR:$s), + "mkdadj.s\t$r, $s", []>, Requires<[HasSingleFloat]> { + let t = 0x0D; +} + +def MKSADJ_S : RRR_Inst<0x00, 0x0A, 0x0F, (outs FPR:$r), (ins FPR:$s), + "mksadj.s\t$r, $s", []>, Requires<[HasSingleFloat]> { + let t = 0x0C; +} + +// FP move instructions +def MOV_S : RRR_Inst<0x00, 0x0A, 0x0f, (outs FPR:$r), (ins FPR:$s), + "mov.s\t$r, $s", + [(set FPR:$r, (Xtensa_movs FPR:$s))]>, Requires<[HasSingleFloat]> { + let t = 0x00; +} + +def MOVEQZ_S : RRR_Inst<0x00, 0x0B, 0x08, (outs FPR:$r), (ins FPR:$s, AR:$t), + "moveqz.s\t$r, $s, $t", []>, Requires<[HasSingleFloat]>; + +def MOVF_S : RRR_Inst<0x00, 0x0B, 0x0C, (outs FPR:$r), (ins FPR:$s, BR:$t), + "movf.s\t$r, $s, $t", []>, Requires<[HasBoolean, HasSingleFloat]>; + +def MOVGEZ_S : RRR_Inst<0x00, 0x0B, 0x0B, (outs FPR:$r), (ins FPR:$s, AR:$t), + "movgez.s\t$r, $s, $t", []>, Requires<[HasSingleFloat]>; + +def MOVLTZ_S : RRR_Inst<0x00, 0x0B, 0x0A, (outs FPR:$r), (ins FPR:$s, AR:$t), + "movltz.s\t$r, $s, $t", []>, Requires<[HasSingleFloat]>; + +def MOVNEZ_S : RRR_Inst<0x00, 0x0B, 0x09, (outs FPR:$r), (ins FPR:$s, AR:$t), + "movnez.s\t$r, $s, $t", []>, Requires<[HasSingleFloat]>; + +def MOVT_S : RRR_Inst<0x00, 0x0B, 0x0D, (outs FPR:$r), (ins FPR:$s, BR:$t), + "movt.s\t$r, $s, $t", []>, Requires<[HasBoolean, HasSingleFloat]>; + +// FP multipy-sub +def MSUB_S : RRR_Inst<0x00, 0x0A, 0x05, (outs FPR:$r), (ins FPR:$a, FPR:$s, FPR:$t), + "msub.s\t$r, $s, $t", + [(set FPR:$r, (Xtensa_msub FPR:$a, FPR:$s, FPR:$t))]>, Requires<[HasSingleFloat]> { + let isCommutable = 0; + let isReMaterializable = 0; + let Constraints = "$r = $a"; +} + +def NEXP01_S : RRR_Inst<0x00, 0x0A, 0x0F, (outs FPR:$r), (ins FPR:$s), + "nexp01.s\t$r, $s", []>, Requires<[HasSingleFloat]> { + let t = 0x0B; +} + +def NEG_S : RRR_Inst<0x00, 0x0A, 0x0F, (outs FPR:$r), (ins FPR:$s), + "neg.s\t$r, $s", + [(set FPR:$r, (fneg FPR:$s))]>, Requires<[HasSingleFloat]> { + let t = 0x06; +} + +def RECIP0_S : RRR_Inst<0x00, 0x0A, 0x0F, (outs FPR:$r), (ins FPR:$s), + "recip0.s\t$r, $s", []>, Requires<[HasSingleFloat]> { + let t = 0x08; +} + +def RFR : RRR_Inst<0x00, 0x0A, 0x0f, (outs AR:$r), (ins FPR:$s), + "rfr\t$r, $s", + [(set AR:$r, (bitconvert FPR:$s))]>, Requires<[HasSingleFloat]> { + let t = 0x04; +} + +def ROUND_S : RRR_Inst<0x00, 0x0A, 0x08, (outs AR:$r), (ins FPR:$s, uimm4:$imm), + "round.s\t$r, $s, $imm", []>, Requires<[HasSingleFloat]> { + bits<4> imm; + + let t = imm; +} + +def RSQRT0_S : RRR_Inst<0x00, 0x0A, 0x0F, (outs FPR:$r), (ins FPR:$s), + "rsqrt0.s\t$r, $s", []>, Requires<[HasSingleFloat]> { + let t = 0x0A; +} + +def SQRT0_S : RRR_Inst<0x00, 0x0A, 0x0F, (outs FPR:$r), (ins FPR:$s), + "sqrt0.s\t$r, $s", []>, Requires<[HasSingleFloat]> { + let t = 0x09; +} + +def TRUNC_S : RRR_Inst<0x00, 0x0A, 0x09, (outs AR:$r), (ins FPR:$s, uimm4:$imm), + "trunc.s\t$r, $s, $imm", []>, Requires<[HasSingleFloat]> { + bits<4> imm; + + let t = imm; +} + +def : Pat<(i32 (any_fp_to_sint FPR:$s)), (TRUNC_S FPR:$s, 0)>; + +def UFLOAT_S : RRR_Inst<0x00, 0x0A, 0x0D, (outs FPR:$r), (ins AR:$s, uimm4:$imm), + "ufloat.s\t$r, $s, $imm", []>, Requires<[HasSingleFloat]> { + bits<4> imm; + + let t = imm; +} + +def : Pat<(f32 (uint_to_fp AR:$s)), (UFLOAT_S AR:$s, 0)>; + +def UTRUNC_S : RRR_Inst<0x00, 0x0A, 0x0e, (outs AR:$r), (ins FPR:$s, uimm4:$imm), + "utrunc.s\t$r, $s, $imm", []>, Requires<[HasSingleFloat]> { + bits<4> imm; + + let t = imm; +} + +def : Pat<(i32 (any_fp_to_uint FPR:$s)), (UTRUNC_S FPR:$s, 0)>; + +def WFR : RRR_Inst<0x00, 0x0A, 0x0f, (outs FPR:$r), (ins AR:$s), + "wfr\t$r, $s", + [(set FPR:$r, (bitconvert AR:$s))]>, Requires<[HasSingleFloat]> { + let t = 0x05; +} + +// FP select operations +let usesCustomInserter = 1, Predicates = [HasSingleFloat] in { + def SELECT_CC_FP_INT : Pseudo<(outs AR:$dst), (ins FPR:$lhs, FPR:$rhs, AR:$t, AR:$f, i32imm:$cond), + "!select_cc_fp_int $dst, $lhs, $rhs, $t, $f, $cond", + [(set AR:$dst, (Xtensa_select_cc_fp FPR:$lhs, FPR:$rhs, AR:$t, AR:$f, imm:$cond))]>; + def SELECT_CC_INT_FP : Pseudo<(outs FPR:$dst), (ins AR:$lhs, AR:$rhs, FPR:$t, FPR:$f, i32imm:$cond), + "!select_cc_int_fp $dst, $lhs, $rhs, $t, $f, $cond", + [(set FPR:$dst, (Xtensa_select_cc_fp AR:$lhs, AR:$rhs, FPR:$t, FPR:$f, imm:$cond))]>; + def SELECT_CC_FP_FP : Pseudo<(outs FPR:$dst), (ins FPR:$lhs, FPR:$rhs, FPR:$t, FPR:$f, i32imm:$cond), + "!select_cc_fp_fp $dst, $lhs, $rhs, $t, $f, $cond", + [(set FPR:$dst, (Xtensa_select_cc_fp FPR:$lhs, FPR:$rhs, FPR:$t, FPR:$f, imm:$cond))]>; +} + +// FP brcc pesudo operation +let usesCustomInserter = 1, isBranch = 1, isTerminator = 1, isBarrier = 1, Predicates = [HasSingleFloat] in { + def BRCC_FP : Pseudo<(outs), (ins i32imm:$cond, FPR:$lhs, FPR:$rhs, brtarget:$target), + "!brcc_fp $cond, $lhs, $rhs, $target", + [(Xtensa_brcc_fp imm:$cond, FPR:$lhs, FPR:$rhs, bb:$target)]>; +} + +//===----------------------------------------------------------------------===// +// Loop Instructions +//===----------------------------------------------------------------------===// + +def LOOP : RRI8_Inst<0x06, (outs), (ins AR:$s, ltarget:$target), + "loop\t$s, $target", []>, Requires<[HasLoop]> { + bits<8> target; + + let r = 0x08; + let t = 0x07; + let imm8 = target; +} + +def LOOPGTZ : RRI8_Inst<0x06, (outs), (ins AR:$s, ltarget:$target), + "loopgtz\t$s, $target", []>, Requires<[HasLoop]> { + bits<8> target; + + let r = 0x0A; + let t = 0x07; + let imm8 = target; +} + +def LOOPNEZ : RRI8_Inst<0x06, (outs), (ins AR:$s, ltarget:$target), + "loopnez\t$s, $target", []>, Requires<[HasLoop]> { + bits<8> target; + + let r = 0x09; + let t = 0x07; + let imm8 = target; +} + +let isTerminator = 1, isBarrier = 1, hasSideEffects = 1, Size = 3 in { + def LOOPINIT : Pseudo<(outs AR:$elts), (ins AR:$eltsin), + "!loopinit $elts, $eltsin", [(set AR:$elts, (int_start_loop_iterations AR:$eltsin))]>; +} + +// LOOPSTART pseudo instruction reserves 9 bytes for LOOP operation and NOP operations for possible alignment. +let isTerminator = 1, isBarrier = 1, hasSideEffects = 1, Size = 9 in { + def LOOPSTART : Pseudo<(outs), (ins AR:$s, brtarget:$target), + "!loopstart $s, $target", []>; +} + +// LOOPEND pseudo instruction reserves 6 bytes for Jump and NOP operations. +let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 0, Size = 6 in { + def LOOPEND : Pseudo<(outs), (ins brtarget:$target), + "!loopend $target", [(Xtensa_loopend bb:$target)]>; +} + +let isTerminator = 1, isBarrier = 1, hasSideEffects = 1, Size = 3 in { + def LOOPDEC : Pseudo<(outs AR:$eltsout), (ins AR:$eltsin), + "!loopdec $eltsout, $eltsin", [(set AR:$eltsout, (Xtensa_loopdec AR:$eltsin))]>; +} + +let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 0, Size = 3 in { + def LOOPBR : Pseudo<(outs), (ins AR:$elts, brtarget:$target), + "!loopbr $elts, $target", [(Xtensa_loopbr AR:$elts, bb:$target)]>; +} + +//===----------------------------------------------------------------------===// +// SEXT Instructions +//===----------------------------------------------------------------------===// + +def SEXT : RRR_Inst<0x00, 0x03, 0x02, (outs AR:$r), (ins AR:$s, seimm7_22:$imm), + "sext\t$r, $s, $imm", []>, Requires<[HasSEXT]> { + bits<4> imm; + + let t = imm; +} + +//===----------------------------------------------------------------------===// +// NSA Instructions +//===----------------------------------------------------------------------===// + +def NSA : RRR_Inst<0x00, 0x00, 0x04, (outs AR:$t), (ins AR:$s), + "nsa\t$t, $s", []>, Requires<[HasNSA]> { + let r = 0xE; +} + +def NSAU : RRR_Inst<0x00, 0x00, 0x04, (outs AR:$t), (ins AR:$s), + "nsau\t$t, $s", []>, Requires<[HasNSA]> { + let r = 0xF; +} + +//===----------------------------------------------------------------------===// +// Mul16 Instructions +//===----------------------------------------------------------------------===// + +let Predicates = [HasMul16] in { + def MUL16S : RRR_Inst<0x00, 0x01, 0x0D, (outs AR:$r), (ins AR:$s, AR:$t), + "mul16s\t$r, $s, $t", []>; + def MUL16U : RRR_Inst<0x00, 0x01, 0x0C, (outs AR:$r), (ins AR:$s, AR:$t), + "mul16u\t$r, $s, $t", []>; +} + +//===----------------------------------------------------------------------===// +// Mul32 Instructions +//===----------------------------------------------------------------------===// + +def MULL : ArithLogic_RRR<0x08, 0x02, "mull", mul, 1>, Requires<[HasMul32]>; +def MULUH : ArithLogic_RRR<0x0A, 0x02, "muluh", mulhu, 1>, Requires<[HasMul32High]>; +def MULSH : ArithLogic_RRR<0x0B, 0x02, "mulsh", mulhs, 1>, Requires<[HasMul32High]>; + +//===----------------------------------------------------------------------===// +// Div32 Instructions +//===----------------------------------------------------------------------===// + +def QUOS : ArithLogic_RRR<0x0D, 0x02, "quos", sdiv>, Requires<[HasDiv32]>; +def QUOU : ArithLogic_RRR<0x0C, 0x02, "quou", udiv>, Requires<[HasDiv32]>; +def REMS : ArithLogic_RRR<0x0F, 0x02, "rems", srem>, Requires<[HasDiv32]>; +def REMU : ArithLogic_RRR<0x0E, 0x02, "remu", urem>, Requires<[HasDiv32]>; + +//===----------------------------------------------------------------------===// +// S32C1I +//===----------------------------------------------------------------------===// + +let mayStore = 1, mayLoad = 1, Predicates = [HasS32C1I] in { + def S32C1I : RRI8_Inst<0x02, (outs AR:$a), (ins AR:$t, mem32:$addr), + "s32c1i\t$t, $addr", []> { + bits<12> addr; + + let r = 0x0e; + let Uses = [SCOMPARE1]; + let Constraints = "$a = $t"; + let imm8{7-0} = addr{11-4}; + let s{3-0} = addr{3-0}; + } +} + +//===----------------------------------------------------------------------===// +// Debug instructions +//===----------------------------------------------------------------------===// + +let isBarrier = 1, isTerminator = 1 in { + def BREAK : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins uimm4:$s, uimm4:$t), + "break\t$s, $t", []>, Requires<[HasDebug]> { + let r = 0x04; + } + + def BREAK_N : RRRN_Inst<0x0C, (outs), (ins uimm4:$imm), + "break.n\t$imm", []>, Requires<[HasDensity, HasDebug]> { + bits<4> imm; + + let r = 0xf; + let s = imm; + let t = 0x2; + } +} + +def : Pat<(trap), (BREAK (i32 1), (i32 15))>; + +//===----------------------------------------------------------------------===// +// Exception feature instructions +//===----------------------------------------------------------------------===// + +def EXCW : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins), + "excw", []>, Requires<[HasException]> { + let r = 0x2; + let s = 0x0; + let t = 0x8; +} + +def RFDE : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins), + "rfde", []>, Requires<[HasException]> { + let r = 0x3; + let s = 0x2; + let t = 0x0; +} + + +def RFE : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins), + "rfe", []>, Requires<[HasException]> { + let r = 0x3; + let s = 0x0; + let t = 0x0; +} + +def SYSCALL : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins), + "syscall", []>, Requires<[HasException]> { + let r = 0x5; + let s = 0x0; + let t = 0x0; +} + +//===----------------------------------------------------------------------===// +// Interrupt feature instructions +//===----------------------------------------------------------------------===// + +def RSIL : RRR_Inst<0x00, 0x00, 0x00, (outs AR:$t), (ins uimm4:$imm), + "rsil\t$t, $imm", []>, Requires<[HasInterrupt]> { + bits<4> imm; + + let r = 0x6; + let s = imm{3-0}; +} + +def WAITI : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins uimm4:$imm), + "waiti\t$imm", []>, Requires<[HasInterrupt]> { + bits<4> imm; + + let r = 0x7; + let s = imm{3-0}; + let t = 0; +} + +def RFI : RRR_Inst<0x00, 0x00, 0x00, (outs), (ins uimm4:$imm), + "rfi\t$imm", []>, Requires<[HasInterrupt]> { + bits<4> imm; + + let r = 0x3; + let s = imm{3-0}; + let t = 0x1; +} + +//===----------------------------------------------------------------------===// +// Region Protection feature instructions +//===----------------------------------------------------------------------===// + +def WDTLB : RRR_Inst<0x00, 0x00, 0x05, (outs AR:$t), (ins AR:$s), + "wdtlb\t$t, $s", []>, Requires<[HasRegionProtection]> { + let r = 0xE; +} + +def WITLB : RRR_Inst<0x00, 0x00, 0x05, (outs AR:$t), (ins AR:$s), + "witlb\t$t, $s", []>, Requires<[HasRegionProtection]> { + let r = 0x6; +} + +//===----------------------------------------------------------------------===// +// Illegal instructions +//===----------------------------------------------------------------------===// + +let isBarrier = 1, isTerminator = 1 in { + def ILL : CALLX_Inst<0x00, 0x00, 0x00, (outs), (ins), + "ill", []> { + let m = 0x0; + let n = 0x0; + let r = 0; + let s = 0; + } + + def ILL_N : RRRN_Inst<0x0D, (outs), (ins), + "ill.n", []>, Requires<[HasDensity]> { + let r = 0xf; + let s = 0x0; + let t = 0x6; + } +} + +//===----------------------------------------------------------------------===// +// Atomic patterns +//===----------------------------------------------------------------------===// + +def : Pat<(i32 (atomic_load_8 addr_ish1:$addr)), (L8UI addr_ish1:$addr)>; +def : Pat<(i32 (atomic_load_16 addr_ish2:$addr)), (L16UI addr_ish2:$addr)>; +def : Pat<(i32 (atomic_load_32 addr_ish4:$addr)), (L32I addr_ish4:$addr)>; + +def : Pat<(atomic_store_8 addr_ish1:$addr, AR:$t), (S8I AR:$t, addr_ish1:$addr)>; +def : Pat<(atomic_store_16 addr_ish2:$addr, AR:$t), (S16I AR:$t, addr_ish2:$addr)>; +def : Pat<(atomic_store_32 addr_ish4:$addr, AR:$t), (S32I AR:$t, addr_ish4:$addr)>; + +let usesCustomInserter = 1, Predicates = [HasS32C1I] in { + def ATOMIC_CMP_SWAP_8_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$cmp, AR:$swap), + "!atomic_cmp_swap_8_p, $dst, $ptr, $cmp, $swap", + [(set AR:$dst, (atomic_cmp_swap_8 AR:$ptr, AR:$cmp, AR:$swap))]>; + def ATOMIC_CMP_SWAP_16_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$cmp, AR:$swap), + "!atomic_cmp_swap_16_p, $dst, $ptr, $cmp, $swap", + [(set AR:$dst, (atomic_cmp_swap_16 AR:$ptr, AR:$cmp, AR:$swap))]>; + def ATOMIC_CMP_SWAP_32_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$cmp, AR:$swap), + "!atomic_cmp_swap_32_p, $dst, $ptr, $cmp, $swap", + [(set AR:$dst, (atomic_cmp_swap_32 AR:$ptr, AR:$cmp, AR:$swap))]>; + + def ATOMIC_SWAP_8_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$swap), + "!atomic_swap_8_p, $dst, $ptr, $swap", + [(set AR:$dst, (atomic_swap_8 AR:$ptr, AR:$swap))]>; + def ATOMIC_SWAP_16_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$swap), + "!atomic_swap_16_p, $dst, $ptr, $swap", + [(set AR:$dst, (atomic_swap_16 AR:$ptr, AR:$swap))]>; + def ATOMIC_SWAP_32_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$swap), + "!atomic_swap_32_p, $dst, $ptr, $swap", + [(set AR:$dst, (atomic_swap_32 AR:$ptr, AR:$swap))]>; + + def ATOMIC_LOAD_ADD_8_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_add_8_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_add_8 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_ADD_16_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_add_16_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_add_16 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_ADD_32_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_add_32_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_add_32 AR:$ptr, AR:$arg))]>; + + def ATOMIC_LOAD_SUB_8_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_sub_8_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_sub_8 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_SUB_16_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_sub_16_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_sub_16 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_SUB_32_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_sub_32_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_sub_32 AR:$ptr, AR:$arg))]>; + + def ATOMIC_LOAD_AND_8_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_and_8_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_and_8 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_AND_16_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_and_16_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_and_16 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_AND_32_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_and_32_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_and_32 AR:$ptr, AR:$arg))]>; + + def ATOMIC_LOAD_OR_8_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_or_8_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_or_8 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_OR_16_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_or_16_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_or_16 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_OR_32_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_or_32_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_or_32 AR:$ptr, AR:$arg))]>; + + def ATOMIC_LOAD_XOR_8_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_xor_8_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_xor_8 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_XOR_16_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_xor_16_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_xor_16 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_XOR_32_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_xor_32_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_xor_32 AR:$ptr, AR:$arg))]>; + + def ATOMIC_LOAD_NAND_8_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_nand_8_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_nand_8 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_NAND_16_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_nand_16_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_nand_16 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_NAND_32_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_nand_32_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_nand_32 AR:$ptr, AR:$arg))]>; + + def ATOMIC_LOAD_MIN_8_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_min_8_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_min_8 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_MIN_16_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_min_16_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_min_16 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_MIN_32_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_min_32_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_min_32 AR:$ptr, AR:$arg))]>; + + def ATOMIC_LOAD_MAX_8_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_max_8_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_max_8 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_MAX_16_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_max_16_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_max_16 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_MAX_32_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_max_32_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_max_32 AR:$ptr, AR:$arg))]>; + + def ATOMIC_LOAD_UMIN_8_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_umin_8_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_umin_8 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_UMIN_16_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_umin_16_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_umin_16 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_UMIN_32_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_umin_32_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_umin_32 AR:$ptr, AR:$arg))]>; + + def ATOMIC_LOAD_UMAX_8_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_umax_8_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_umax_8 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_UMAX_16_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_umax_16_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_umax_16 AR:$ptr, AR:$arg))]>; + def ATOMIC_LOAD_UMAX_32_P : Pseudo<(outs AR:$dst), (ins AR:$ptr, AR:$arg), + "!atomic_load_umax_32_p, $dst, $ptr, $arg", + [(set AR:$dst, (atomic_load_umax_32 AR:$ptr, AR:$arg))]>; +} + +//===----------------------------------------------------------------------===// +// Xtensa CONSTPOOL_ENTRY +//===----------------------------------------------------------------------===// + +// An operand for the CONSTPOOL_ENTRY pseudo-instruction. +def cpinst_operand : Operand { + // let PrintMethod = "printCPInstOperand"; +} + +// CONSTPOOL_ENTRY - This instruction represents a floating constant pool in +// the function. The first operand is the ID# for this instruction, the second +// is the index into the MachineConstantPool that this is, the third is the +// size in bytes of this constant pool entry. +// +let hasSideEffects = 0, isNotDuplicable = 1 in +def CONSTPOOL_ENTRY : +Pseudo<(outs), (ins cpinst_operand:$instid, cpinst_operand:$cpidx, + i32imm:$size), "foo", []>; + + +//===----------------------------------------------------------------------===// +// Xtensa ESP32S2 Instructions +//===----------------------------------------------------------------------===// +let Predicates = [HasESP32S2Ops] in { + def WR_MASK_GPIO_OUT : RRR_Inst<0x0, 0x06, 0x0, (outs), (ins AR:$s, AR:$t), + "wr_mask_gpio_out\t$s, $t", []> { + let r = 0x2; + } + + def SET_BIT_GPIO_OUT : RRR_Inst<0x0, 0x06, 0x0, (outs), (ins select_256:$imm), + "set_bit_gpio_out\t$imm", []> { + bits<8> imm; + + let r = 0x1; + let s = imm{7-4}; + let t = imm{3-0}; + } + + def CLR_BIT_GPIO_OUT : RRR_Inst<0x0, 0x06, 0x0, (outs), (ins select_256:$imm), + "clr_bit_gpio_out\t$imm", []> { + bits<8> imm; + + let r = 0x0; + let s = imm{7-4}; + let t = imm{3-0}; + } + + def GET_GPIO_IN : RRR_Inst<0x0, 0x06, 0x0, (outs AR:$t), (ins), + "get_gpio_in\t$t", []> { + let r = 0x3; + let s = 0x0; + } +} + +//===----------------------------------------------------------------------===// +// Xtensa ESP32S3 Instructions +//===----------------------------------------------------------------------===// +let Predicates = [HasESP32S3Ops] in { + def EE_WR_MASK_GPIO_OUT : RRR_Inst<0x04, 0x02, 0x07, (outs), (ins AR:$t, AR:$s), + "ee.wr_mask_gpio_out\t$t, $s", []> { + let r = 0x4; + } + + def EE_SET_BIT_GPIO_OUT : RRR_Inst<0x04, 0x05, 0x07, (outs), (ins select_256:$imm), + "ee.set_bit_gpio_out\t$imm", []> { + bits<8> imm; + + let r = 0x4; + let s = imm{7-4}; + let t = imm{3-0}; + } + + def EE_CLR_BIT_GPIO_OUT : RRR_Inst<0x04, 0x06, 0x07, (outs), (ins select_256:$imm), + "ee.clr_bit_gpio_out\t$imm", []> { + bits<8> imm; + + let r = 0x4; + let s = imm{7-4}; + let t = imm{3-0}; + } + + def EE_GET_GPIO_IN : RRR_Inst<0x04, 0x05, 0x06, (outs AR:$t), (ins), + "ee.get_gpio_in\t$t", []> { + let r = 0x0; + let s = 0x8; + } +} + +//===----------------------------------------------------------------------===// +// DSP Instructions +//===----------------------------------------------------------------------===// +include "XtensaDSPInstrInfo.td" diff --git a/llvm/lib/Target/Xtensa/XtensaMCInstLower.cpp b/llvm/lib/Target/Xtensa/XtensaMCInstLower.cpp new file mode 100644 index 0000000000000..d3dc082617e15 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaMCInstLower.cpp @@ -0,0 +1,135 @@ +//===- XtensaMCInstLower.cpp - Convert Xtensa MachineInstr to MCInst ------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains code to lower Xtensa MachineInstrs to their corresponding +// MCInst records. +// +//===----------------------------------------------------------------------===// + +#include "XtensaMCInstLower.h" +#include "MCTargetDesc/XtensaMCExpr.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/IR/Mangler.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCStreamer.h" + +using namespace llvm; + +XtensaMCInstLower::XtensaMCInstLower(MCContext &ctx, + XtensaAsmPrinter &asmPrinter) + : Ctx(ctx), Printer(asmPrinter) {} + +MCSymbol * +XtensaMCInstLower::GetExternalSymbolSymbol(const MachineOperand &MO) const { + return Printer.GetExternalSymbolSymbol(MO.getSymbolName()); +} + +MCSymbol * +XtensaMCInstLower::GetJumpTableSymbol(const MachineOperand &MO) const { + return Printer.GetJTISymbol(MO.getIndex()); +} + +MCSymbol * +XtensaMCInstLower::GetConstantPoolIndexSymbol(const MachineOperand &MO) const { + // Create a symbol for the name. + return Printer.GetCPISymbol(MO.getIndex()); +} + +MCOperand +XtensaMCInstLower::LowerSymbolOperand(const MachineOperand &MO, + MachineOperand::MachineOperandType MOTy, + unsigned Offset) const { + const MCSymbol *Symbol; + XtensaMCExpr::VariantKind Kind = XtensaMCExpr::VK_Xtensa_None; + + switch (MOTy) { + case MachineOperand::MO_MachineBasicBlock: + Symbol = MO.getMBB()->getSymbol(); + break; + case MachineOperand::MO_GlobalAddress: + Symbol = Printer.getSymbol(MO.getGlobal()); + Offset += MO.getOffset(); + break; + case MachineOperand::MO_BlockAddress: + Symbol = Printer.GetBlockAddressSymbol(MO.getBlockAddress()); + Offset += MO.getOffset(); + break; + case MachineOperand::MO_ExternalSymbol: + Symbol = GetExternalSymbolSymbol(MO); + Offset += MO.getOffset(); + break; + case MachineOperand::MO_JumpTableIndex: + Symbol = GetJumpTableSymbol(MO); + break; + case MachineOperand::MO_ConstantPoolIndex: + Symbol = GetConstantPoolIndexSymbol(MO); + Offset += MO.getOffset(); + break; + default: + llvm_unreachable(""); + } + + const MCExpr *ME = + MCSymbolRefExpr::create(Symbol, MCSymbolRefExpr::VK_None, Ctx); + + ME = XtensaMCExpr::create(ME, Kind, Ctx); + + if (Offset) { + // Assume offset is never negative. + assert(Offset > 0); + + const MCConstantExpr *OffsetExpr = MCConstantExpr::create(Offset, Ctx); + ME = MCBinaryExpr::createAdd(ME, OffsetExpr, Ctx); + } + + return MCOperand::createExpr(ME); +} + +MCOperand XtensaMCInstLower::lowerOperand(const MachineOperand &MO, + unsigned Offset) const { + MachineOperand::MachineOperandType MOTy = MO.getType(); + + switch (MOTy) { + case MachineOperand::MO_Register: + // Ignore all implicit register operands. + if (MO.isImplicit()) + break; + return MCOperand::createReg(MO.getReg()); + case MachineOperand::MO_Immediate: + return MCOperand::createImm(MO.getImm() + Offset); + case MachineOperand::MO_RegisterMask: + break; + case MachineOperand::MO_MachineBasicBlock: + case MachineOperand::MO_GlobalAddress: + case MachineOperand::MO_ExternalSymbol: + case MachineOperand::MO_JumpTableIndex: + case MachineOperand::MO_ConstantPoolIndex: + case MachineOperand::MO_BlockAddress: + return LowerSymbolOperand(MO, MOTy, Offset); + default: + llvm_unreachable("unknown operand type"); + } + + return MCOperand(); +} + +void XtensaMCInstLower::lower(const MachineInstr *MI, MCInst &OutMI) const { + OutMI.setOpcode(MI->getOpcode()); + + for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) { + const MachineOperand &MO = MI->getOperand(i); + MCOperand MCOp = lowerOperand(MO); + + if (MCOp.isValid()) + OutMI.addOperand(MCOp); + } +} diff --git a/llvm/lib/Target/Xtensa/XtensaMCInstLower.h b/llvm/lib/Target/Xtensa/XtensaMCInstLower.h new file mode 100644 index 0000000000000..344c28058adcf --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaMCInstLower.h @@ -0,0 +1,54 @@ +//===- XtensaMCInstLower.h - Lower MachineInstr to MCInst ------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSAMCINSTLOWER_H +#define LLVM_LIB_TARGET_XTENSA_XTENSAMCINSTLOWER_H + +#include "XtensaAsmPrinter.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DataTypes.h" + +namespace llvm { +class MCContext; +class MCInst; +class MCOperand; +class MCSymbol; +class MachineInstr; +class MachineOperand; +class XtensaAsmPrinter; + +class LLVM_LIBRARY_VISIBILITY XtensaMCInstLower { + MCContext &Ctx; + XtensaAsmPrinter &Printer; + +public: + XtensaMCInstLower(MCContext &ctx, XtensaAsmPrinter &asmPrinter); + + // Lower MachineInstr MI to MCInst OutMI. + void lower(const MachineInstr *MI, MCInst &OutMI) const; + + // Return an MCOperand for MO. Return an empty operand if MO is implicit. + MCOperand lowerOperand(const MachineOperand &MO, unsigned Offset = 0) const; + +private: + MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const; + + MCSymbol *GetJumpTableSymbol(const MachineOperand &MO) const; + + MCSymbol *GetConstantPoolIndexSymbol(const MachineOperand &MO) const; + + MCOperand LowerSymbolOperand(const MachineOperand &MO, + MachineOperand::MachineOperandType MOTy, + unsigned Offset) const; +}; +} // end namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_XTENSAMCINSTLOWER_H */ diff --git a/llvm/lib/Target/Xtensa/XtensaMachineFunctionInfo.cpp b/llvm/lib/Target/Xtensa/XtensaMachineFunctionInfo.cpp new file mode 100644 index 0000000000000..1a285b2aa5310 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaMachineFunctionInfo.cpp @@ -0,0 +1,19 @@ +//===- XtensaMachineFunctionInfo.cpp - Private data used for Xtensa -------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "XtensaMachineFunctionInfo.h" +//#include "MCTargetDesc/XtensaBaseInfo.h" +#include "XtensaInstrInfo.h" +#include "XtensaSubtarget.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Function.h" + +using namespace llvm; diff --git a/llvm/lib/Target/Xtensa/XtensaMachineFunctionInfo.h b/llvm/lib/Target/Xtensa/XtensaMachineFunctionInfo.h new file mode 100644 index 0000000000000..49adc260856ae --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaMachineFunctionInfo.h @@ -0,0 +1,60 @@ +//==- XtensaMachineFunctionInfo.h - Xtensa machine function info --*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares Xtensa-specific per-machine-function information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSAMACHINEFUNCTIONINFO_H +#define LLVM_LIB_TARGET_XTENSA_XTENSAMACHINEFUNCTIONINFO_H + +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { + +class XtensaFunctionInfo : public MachineFunctionInfo { + MachineFunction &MF; + + unsigned VarArgsFirstGPR; + int VarArgsStackOffset; + unsigned VarArgsFrameIndex; + bool SaveFrameRegister = false; + +public: + explicit XtensaFunctionInfo(MachineFunction &MF) + : MF(MF), VarArgsFirstGPR(0), VarArgsStackOffset(0), + VarArgsFrameIndex(0) { + MF.setAlignment(Align(4)); + } + + unsigned getVarArgsFirstGPR() const { return VarArgsFirstGPR; } + void setVarArgsFirstGPR(unsigned GPR) { VarArgsFirstGPR = GPR; } + + int getVarArgsStackOffset() const { return VarArgsStackOffset; } + void setVarArgsStackOffset(int Offset) { VarArgsStackOffset = Offset; } + + // Get and set the frame index of the first stack vararg. + unsigned getVarArgsFrameIndex() const { return VarArgsFrameIndex; } + void setVarArgsFrameIndex(unsigned FI) { VarArgsFrameIndex = FI; } + + bool isSaveFrameRegister() const { return SaveFrameRegister; } + void setSaveFrameRegister() { SaveFrameRegister = true; } + + // TODO: large frame size definition should be specified more precisely + bool isLargeFrame() { + return (MF.getFrameInfo().estimateStackSize(MF) > 512) ? true : false; + } +}; + +} // namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_XTENSAMACHINEFUNCTIONINFO_H */ diff --git a/llvm/lib/Target/Xtensa/XtensaOperands.td b/llvm/lib/Target/Xtensa/XtensaOperands.td new file mode 100644 index 0000000000000..620aeee000518 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaOperands.td @@ -0,0 +1,260 @@ +//===- XtensaOperands.td - Xtensa instruction operands -------*- tblgen-*--===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Immediate operands with a shared generic render method. +class ImmAsmOperand : AsmOperandClass { + let Name = name; + let RenderMethod = "addImmOperands"; + let DiagnosticType = !strconcat("Invalid", name); +} + +class Immediate + : Operand, ImmLeaf { + let PrintMethod = "print"#asmop; + let ParserMatchClass = !cast(asmop); +} + +// imm8 predicate - Immediate in the range [-128,127] +def Imm8_AsmOperand : ImmAsmOperand<"Imm8">; +def imm8 : Immediate= -128 && Imm <= 127; }], "Imm8_AsmOperand"> { + let EncoderMethod = "getImm8OpValue"; + let DecoderMethod = "decodeImm8Operand"; +} + +// imm8_sh8 predicate - Immediate in the range [-32768,32512] with (bits[7-0] == 0) +// imm8 value left shifted by 8 bits +def Imm8_sh8_AsmOperand : ImmAsmOperand<"Imm8_sh8">; +def imm8_sh8 : Immediate= -32768 && Imm <= 32512 && ((Imm & 0xFF) == 0); }], + "Imm8_sh8_AsmOperand"> { + let EncoderMethod = "getImm8_sh8OpValue"; + let DecoderMethod = "decodeImm8_sh8Operand"; +} + +// imm8n_7 predicate - Immediate in the range [-8,7] +def Imm8n_7_AsmOperand: ImmAsmOperand<"Imm8n_7">; +def imm8n_7: Immediate= -8 && Imm <= 7; }], "Imm8n_7_AsmOperand"> { + let EncoderMethod = "getImm8n_7OpValue"; + let DecoderMethod = "decodeImm8n_7Operand"; +} + +// imm64n_4n predicate - Immediate in the range [-64,-4] +def Imm64n_4n_AsmOperand: ImmAsmOperand<"Imm64n_4n">; +def imm64n_4n: Immediate= -64 && Imm <= -4; }], "Imm64n_4n_AsmOperand"> { + let EncoderMethod = "getImm64n_4nOpValue"; + let DecoderMethod = "decodeImm64n_4nOperand"; +} + +// imm12 predicate - Immediate in the range [-2048,2047] +def Imm12_AsmOperand : ImmAsmOperand<"Imm12">; +def imm12 : Immediate= -2048 && Imm <= 2047; }], "Imm12_AsmOperand"> { + let EncoderMethod = "getImm12OpValue"; + let DecoderMethod = "decodeImm12Operand"; +} + +// imm12m predicate - Immediate for MOV operation +def Imm12m_AsmOperand : ImmAsmOperand<"Imm12m">; +def imm12m : Immediate= -2048 && Imm <= 2047; }], "Imm12m_AsmOperand"> { + let EncoderMethod = "getImm12OpValue"; + let DecoderMethod = "decodeImm12Operand"; +} + +// uimm4 predicate - Immediate in the range [0,15] +def Uimm4_AsmOperand : ImmAsmOperand<"Uimm4">; +def uimm4 : Immediate= 0 && Imm <= 15; }], "Uimm4_AsmOperand"> { + let EncoderMethod = "getUimm4OpValue"; + let DecoderMethod = "decodeUimm4Operand"; +} + +// uimm5 predicate - Immediate in the range [0,31] +def Uimm5_AsmOperand : ImmAsmOperand<"Uimm5">; +def uimm5 : Immediate= 0 && Imm <= 31; }], "Uimm5_AsmOperand"> { + let EncoderMethod = "getUimm5OpValue"; + let DecoderMethod = "decodeUimm5Operand"; +} + +// imm1_16 predicate - Immediate in the range [1,16] +def Imm1_16_AsmOperand : ImmAsmOperand<"Imm1_16">; +def imm1_16 : Immediate= 1 && Imm <= 16; }], "Imm1_16_AsmOperand"> { + let EncoderMethod = "getImm1_16OpValue"; + let DecoderMethod = "decodeImm1_16Operand"; +} + +// imm1n_15 predicate - Immediate in the range [-1,15], except 0 +def Imm1n_15_AsmOperand: ImmAsmOperand<"Imm1n_15">; +def imm1n_15: Immediate= -1 && Imm <= 15 && Imm != 0; }], "Imm1n_15_AsmOperand"> { + let EncoderMethod = "getImm1n_15OpValue"; + let DecoderMethod = "decodeImm1n_15Operand"; +} + +// imm32n_95 predicate - Immediate in the range [-32,95] +def Imm32n_95_AsmOperand: ImmAsmOperand<"Imm32n_95">; +def imm32n_95: Immediate= -32 && Imm <= 95; }], "Imm32n_95_AsmOperand"> { + let EncoderMethod = "getImm32n_95OpValue"; + let DecoderMethod = "decodeImm32n_95Operand"; +} + +// shimm1_31 predicate - Immediate in the range [1,31] +def Shimm1_31_AsmOperand : ImmAsmOperand<"Shimm1_31">; +def shimm1_31 : Immediate= 1 && Imm <= 31; }], "Shimm1_31_AsmOperand"> { + let EncoderMethod = "getShimm1_31OpValue"; + let DecoderMethod = "decodeShimm1_31Operand"; +} + +// Memory offset 0..255 for 8-bit memory accesses +def Offset8m8_AsmOperand : ImmAsmOperand<"Offset8m8">; +def offset8m8 : Immediate= 0 && Imm <= 255; }], + "Offset8m8_AsmOperand">; + +// Memory offset 0..510 for 16-bit memory accesses +def Offset8m16_AsmOperand : ImmAsmOperand<"Offset8m16">; +def offset8m16 : Immediate= 0 && Imm <= 510 && (Imm & 0x1 == 0); }], + "Offset8m16_AsmOperand">; + +// Memory offset 0..1020 for 32-bit memory accesses +def Offset8m32_AsmOperand : ImmAsmOperand<"Offset8m32">; +def offset8m32 : Immediate= 0 && Imm <= 1020 && (Imm & 0x3 == 0); }], + "Offset8m32_AsmOperand">; + +// Memory offset 0..60 for 32-bit memory accesses +def Offset4m32_AsmOperand : ImmAsmOperand<"Offset4m32">; +def offset4m32 : Immediate= 0 && Imm <= 60 && (Imm & 0x3 == 0); }], + "Offset4m32_AsmOperand">; + +// entry_imm12 predicate - Immediate in the range [0,32760], ENTRY parameter +def Entry_Imm12_AsmOperand: ImmAsmOperand<"entry_imm12">; +def entry_imm12: Immediate= 0 && Imm <= 32760 && (Imm & 0x3 == 0); }], "Entry_Imm12_AsmOperand"> { + let EncoderMethod = "getEntry_Imm12OpValue"; + let DecoderMethod = "decodeEntry_Imm12OpValue"; +} + +// b4const predicate - Branch Immediate 4-bit signed operand +def B4const_AsmOperand: ImmAsmOperand<"B4const">; +def b4const: Immediate { + let EncoderMethod = "getB4constOpValue"; + let DecoderMethod = "decodeB4constOperand"; +} + +// b4constu predicate - Branch Immediate 4-bit unsigned operand +def B4constu_AsmOperand: ImmAsmOperand<"B4constu">; +def b4constu: Immediate { + let EncoderMethod = "getB4constuOpValue"; + let DecoderMethod = "decodeB4constuOperand"; +} + +// seimm7_22 predicate - Immediate in the range [7,22] for sign extend +def Seimm7_22_AsmOperand: ImmAsmOperand<"seimm7_22">; +def seimm7_22: Immediate= 7 && Imm <= 22; }], "Seimm7_22_AsmOperand"> { + let EncoderMethod = "getSeimm7_22OpValue"; + let DecoderMethod = "decodeSeimm7_22Operand"; +} + +// select_256 predicate - Immediate in the range [0,255] +def Select_256_AsmOperand: ImmAsmOperand<"Select_256">; +def select_256: Immediate= 0 && Imm <= 255; }], "Select_256_AsmOperand"> { + let EncoderMethod = "getSelect_256OpValue"; + let DecoderMethod = "decodeSelect_256Operand"; +} + +//===----------------------------------------------------------------------===// +// Memory address operands +//===----------------------------------------------------------------------===// + +class mem : Operand { + let MIOperandInfo = (ops AR, offset); + let EncoderMethod = "getMemRegEncoding"; + let OperandType = "OPERAND_MEMORY"; + let PrintMethod = "printMemOperand"; +} + +def mem8 : mem { + let DecoderMethod = "decodeMem8Operand"; +} + +def mem16 : mem { + let DecoderMethod = "decodeMem16Operand"; +} + +def mem32 : mem { + let DecoderMethod = "decodeMem32Operand"; +} + +def mem32n : mem { + let DecoderMethod = "decodeMem32nOperand"; +} + +//Add patterns for future use in stack addressing mode +def addr_ish1 : ComplexPattern; +def addr_ish2 : ComplexPattern; +def addr_ish4 : ComplexPattern; + +//===----------------------------------------------------------------------===// +// Symbolic address operands +//===----------------------------------------------------------------------===// +def XtensaPCRelTargetAsmOperand : AsmOperandClass { + let Name = "PCRelTarget"; + let ParserMethod = "parsePCRelTarget"; + let PredicateMethod = "isImm"; + let RenderMethod = "addImmOperands"; +} + +def pcrel32call : Operand { + let PrintMethod = "printCallOperand"; + let EncoderMethod = "getCallEncoding"; + let DecoderMethod = "decodeCallOperand"; + let ParserMatchClass = XtensaPCRelTargetAsmOperand; +} + +def brtarget : Operand { + let PrintMethod = "printBranchTarget"; + let EncoderMethod = "getBranchTargetEncoding"; + let DecoderMethod = "decodeBranchOperand"; + let ParserMatchClass = XtensaPCRelTargetAsmOperand; +} + +def jumptarget : Operand { + let PrintMethod = "printJumpTarget"; + let EncoderMethod = "getJumpTargetEncoding"; + let DecoderMethod = "decodeJumpOperand"; + let ParserMatchClass = XtensaPCRelTargetAsmOperand; +} + +def ltarget : Operand { + let PrintMethod = "printLoopTarget"; + let EncoderMethod = "getLoopTargetEncoding"; + let DecoderMethod = "decodeLoopOperand"; + let ParserMatchClass = XtensaPCRelTargetAsmOperand; +} + +def L32Rtarget: Operand { + let PrintMethod = "printL32RTarget"; + let EncoderMethod = "getL32RTargetEncoding"; + let DecoderMethod = "decodeL32ROperand"; + let ParserMatchClass = XtensaPCRelTargetAsmOperand; +} diff --git a/llvm/lib/Target/Xtensa/XtensaOperators.td b/llvm/lib/Target/Xtensa/XtensaOperators.td new file mode 100644 index 0000000000000..cdc29be5deb3b --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaOperators.td @@ -0,0 +1,120 @@ +//===- XtensaOperators.td - Xtensa-specific operators ---------*- tblgen-*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Type profiles +//===----------------------------------------------------------------------===// + +def SDT_XtensaCallSeqStart : SDCallSeqStart<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; +def SDT_XtensaCallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; +def SDT_XtensaCall : SDTypeProfile<0, -1, [SDTCisPtrTy<0>]>; +def SDT_XtensaWrapPtr : SDTypeProfile<1, 1, + [SDTCisSameAs<0, 1>, + SDTCisPtrTy<0>]>; + +def SDT_XtensaSelectCC : SDTypeProfile<1, 5, + [SDTCisSameAs<0, 1>, + SDTCisSameAs<2, 3>, + SDTCisVT<5, i32>]>; + +def SDT_XtensaMOVSP : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>, SDTCisVT<0, i32>]>; +def SDT_XtensaBrBool : SDTypeProfile<0, 2, [SDTCisVT<0, i1>, SDTCisVT<1, OtherVT>]>; +def SDT_XtensaBrCCFP : SDTypeProfile<0, 4, [SDTCisVT<0, i32>, SDTCisVT<1, f32>, SDTCisVT<2, f32>, SDTCisVT<3, OtherVT>]>; +def SDT_XtensaCmp : SDTypeProfile<1, 2, [SDTCisVT<0, i1>, SDTCisVT<1, f32>, SDTCisVT<2, f32>]>; +def SDT_XtensaMADD : SDTypeProfile<1, 3, [SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, SDTCisSameAs<0, 3>, SDTCisVT<0, f32>]>; +def SDT_XtensaMOVS : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>, SDTCisVT<0, f32>]>; +def SDT_XtensaSelectCCFP : SDTypeProfile<1, 5, [SDTCisSameAs<0, 3>, SDTCisSameAs<1, 2>, SDTCisSameAs<3, 4>, SDTCisVT<5, i32>]>; +def SDT_XtensaBrJT : SDTypeProfile<0, 2, + [SDTCisPtrTy<0>, SDTCisVT<1, i32>]>; + +def SDT_XtensaSHL : SDTypeProfile<1, 1, [SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; +def SDT_XtensaSRA : SDTypeProfile<1, 1, [SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; +def SDT_XtensaSRL : SDTypeProfile<1, 1, [SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; +def SDT_XtensaSRC : SDTypeProfile<1, 2, [SDTCisVT<0, i32>, SDTCisVT<1, i32>, + SDTCisVT<2, i32>]>; +def SDT_XtensaSSL : SDTypeProfile<0, 1, [SDTCisVT<0, i32>]>; +def SDT_XtensaSSR : SDTypeProfile<0, 1, [SDTCisVT<0, i32>]>; +def SDT_XtensaMEMBARRIER : SDTypeProfile<0, 0, []>; +def SDT_XtensaRUR : SDTypeProfile<1, 1, [SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; + +def SDT_XtensaLoopEnd : SDTypeProfile<0, 1, [SDTCisVT<0, OtherVT>]>; +def SDT_XtensaLoopDec : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>, SDTCisVT<0, i32>]>; +def SDT_XtensaLoopBr : SDTypeProfile<0, 2, [SDTCisVT<0, i32>, SDTCisVT<1, OtherVT>]>; + +//===----------------------------------------------------------------------===// +// Node definitions +//===----------------------------------------------------------------------===// + +def Xtensa_call: SDNode<"XtensaISD::CALL", SDT_XtensaCall, + [SDNPHasChain, SDNPOutGlue, SDNPOptInGlue, SDNPVariadic]>; + +def Xtensa_retflag: SDNode<"XtensaISD::RET_FLAG", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def Xtensa_retWflag: SDNode<"XtensaISD::RETW_FLAG", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; + +def Xtensa_callseq_start: SDNode<"ISD::CALLSEQ_START", SDT_XtensaCallSeqStart, + [SDNPHasChain, SDNPSideEffect, SDNPOutGlue]>; +def Xtensa_callseq_end : SDNode<"ISD::CALLSEQ_END", SDT_XtensaCallSeqEnd, + [SDNPHasChain, SDNPSideEffect, SDNPOptInGlue, + SDNPOutGlue]>; + +def Xtensa_pcrel_wrapper: SDNode<"XtensaISD::PCREL_WRAPPER", SDT_XtensaWrapPtr, []>; + +def Xtensa_select : SDNode<"XtensaISD::SELECT", SDTSelect>; +def Xtensa_select_cc: SDNode<"XtensaISD::SELECT_CC", SDT_XtensaSelectCC, + [SDNPInGlue]>; +def Xtensa_select_cc_fp: SDNode<"XtensaISD::SELECT_CC_FP", SDT_XtensaSelectCCFP, + [SDNPInGlue]>; + +def Xtensa_movsp: SDNode<"XtensaISD::MOVSP", SDT_XtensaMOVSP, + [SDNPInGlue]>; + +def Xtensa_br_t : SDNode<"XtensaISD::BR_T", SDT_XtensaBrBool, + [SDNPHasChain, SDNPInGlue]>; +def Xtensa_br_f : SDNode<"XtensaISD::BR_F", SDT_XtensaBrBool, + [SDNPHasChain, SDNPInGlue]>; +def Xtensa_brcc_fp : SDNode<"XtensaISD::BR_CC_FP", SDT_XtensaBrCCFP, + [SDNPHasChain, SDNPInGlue]>; + +def Xtensa_cmpoeq : SDNode<"XtensaISD::CMPOEQ", SDT_XtensaCmp, [SDNPOutGlue]>; +def Xtensa_cmpolt : SDNode<"XtensaISD::CMPOLT", SDT_XtensaCmp, [SDNPOutGlue]>; +def Xtensa_cmpole : SDNode<"XtensaISD::CMPOLE", SDT_XtensaCmp, [SDNPOutGlue]>; +def Xtensa_cmpueq : SDNode<"XtensaISD::CMPUEQ", SDT_XtensaCmp, [SDNPOutGlue]>; +def Xtensa_cmpult : SDNode<"XtensaISD::CMPULT", SDT_XtensaCmp, [SDNPOutGlue]>; +def Xtensa_cmpule : SDNode<"XtensaISD::CMPULE", SDT_XtensaCmp, [SDNPOutGlue]>; +def Xtensa_cmpuo : SDNode<"XtensaISD::CMPUO", SDT_XtensaCmp, [SDNPOutGlue]>; + +def Xtensa_madd: SDNode<"XtensaISD::MADD", SDT_XtensaMADD, [SDNPInGlue]>; +def Xtensa_msub: SDNode<"XtensaISD::MSUB", SDT_XtensaMADD, [SDNPInGlue]>; +def Xtensa_movs: SDNode<"XtensaISD::MOVS", SDT_XtensaMOVS, [SDNPInGlue]>; + +def Xtensa_shl: SDNode<"XtensaISD::SHL", SDT_XtensaSHL, [SDNPInGlue]>; +def Xtensa_sra: SDNode<"XtensaISD::SRA", SDT_XtensaSRA, [SDNPInGlue]>; +def Xtensa_srl: SDNode<"XtensaISD::SRL", SDT_XtensaSRL, [SDNPInGlue]>; +def Xtensa_src: SDNode<"XtensaISD::SRC", SDT_XtensaSRC, [SDNPInGlue]>; +def Xtensa_ssl: SDNode<"XtensaISD::SSL", SDT_XtensaSSL, [SDNPOutGlue]>; +def Xtensa_ssr: SDNode<"XtensaISD::SSR", SDT_XtensaSSR, [SDNPOutGlue]>; + +def Xtensa_brjt: SDNode<"XtensaISD::BR_JT", SDT_XtensaBrJT, [SDNPHasChain]>; +def Xtensa_callw: SDNode<"XtensaISD::CALLW", SDT_XtensaCall, + [SDNPHasChain, SDNPOutGlue, SDNPOptInGlue, SDNPVariadic]>; +def Xtensa_mem_barrier: SDNode<"XtensaISD::MEMW", SDT_XtensaMEMBARRIER, + [SDNPHasChain, SDNPSideEffect]>; + +def Xtensa_rur: SDNode<"XtensaISD::RUR", SDT_XtensaRUR, + [SDNPInGlue]>; + +def Xtensa_loopend: SDNode<"XtensaISD::LOOPEND", SDT_XtensaLoopEnd, + [SDNPHasChain, SDNPInGlue]>; +def Xtensa_loopdec: SDNode<"XtensaISD::LOOPDEC", SDT_XtensaLoopDec, + [SDNPHasChain, SDNPInGlue]>; +def Xtensa_loopbr: SDNode<"XtensaISD::LOOPBR", SDT_XtensaLoopBr, + [SDNPHasChain, SDNPInGlue]>; diff --git a/llvm/lib/Target/Xtensa/XtensaRegisterInfo.cpp b/llvm/lib/Target/Xtensa/XtensaRegisterInfo.cpp new file mode 100644 index 0000000000000..8f773458583ed --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaRegisterInfo.cpp @@ -0,0 +1,183 @@ +//===- XtensaRegisterInfo.cpp - Xtensa Register Information ---------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the Xtensa implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#include "XtensaRegisterInfo.h" +#include "XtensaInstrInfo.h" +#include "XtensaMachineFunctionInfo.h" +#include "XtensaSubtarget.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +#define DEBUG_TYPE "xtensa-reg-info" + +#define GET_REGINFO_TARGET_DESC +#include "XtensaGenRegisterInfo.inc" + +using namespace llvm; + +XtensaRegisterInfo::XtensaRegisterInfo(const XtensaSubtarget &STI) + : XtensaGenRegisterInfo(Xtensa::A0), Subtarget(STI) {} + +const uint16_t * +XtensaRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { + if (Subtarget.isWinABI()) + return CSRWE_Xtensa_SaveList; + else + return CSR_Xtensa_SaveList; +} + +const uint32_t * +XtensaRegisterInfo::getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID) const { + if (Subtarget.isWinABI()) + return CSRWE_Xtensa_RegMask; + else + return CSR_Xtensa_RegMask; +} + +BitVector XtensaRegisterInfo::getReservedRegs(const MachineFunction &MF) const { + BitVector Reserved(getNumRegs()); + const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering(); + + Reserved.set(Xtensa::A0); + if (TFI->hasFP(MF)) { + // Reserve frame pointer. + Reserved.set(getFrameRegister(MF)); + } + + // Reserve stack pointer. + Reserved.set(Xtensa::SP); + return Reserved; +} + +void XtensaRegisterInfo::eliminateFI(MachineBasicBlock::iterator II, + unsigned OpNo, int FrameIndex, + uint64_t StackSize, + int64_t SPOffset) const { + MachineInstr &MI = *II; + MachineFunction &MF = *MI.getParent()->getParent(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + + const std::vector &CSI = MFI.getCalleeSavedInfo(); + int MinCSFI = 0; + int MaxCSFI = -1; + + if (CSI.size()) { + MinCSFI = CSI[0].getFrameIdx(); + MaxCSFI = CSI[CSI.size() - 1].getFrameIdx(); + } + + // The following stack frame objects are always referenced relative to $sp: + // 1. Outgoing arguments. + // 2. Pointer to dynamically allocated stack space. + // 3. Locations for callee-saved registers. + // 4. Locations for eh data registers. + // Everything else is referenced relative to whatever register + // getFrameRegister() returns. + unsigned FrameReg; + + if ((FrameIndex >= MinCSFI && FrameIndex <= MaxCSFI)) + FrameReg = Xtensa::SP; + else + FrameReg = getFrameRegister(MF); + + // Calculate final offset. + // - There is no need to change the offset if the frame object is one of the + // following: an outgoing argument, pointer to a dynamically allocated + // stack space or a $gp restore location, + // - If the frame object is any of the following, its offset must be adjusted + // by adding the size of the stack: + // incoming argument, callee-saved register location or local variable. + bool IsKill = false; + int64_t Offset; + + Offset = SPOffset + (int64_t)StackSize; + Offset += MI.getOperand(OpNo + 1).getImm(); + + LLVM_DEBUG(errs() << "Offset : " << Offset << "\n" + << "<--------->\n"); + + bool Valid = false; + switch (MI.getOpcode()) { + case Xtensa::L8I_P: + case Xtensa::L8UI: + case Xtensa::S8I: + Valid = (Offset >= 0 && Offset <= 255); + break; + case Xtensa::L16SI: + case Xtensa::L16UI: + case Xtensa::S16I: + Valid = (Offset >= 0 && Offset <= 510); + break; + case Xtensa::LEA_ADD: + Valid = (Offset >= -128 && Offset <= 127); + break; + default: + Valid = (Offset >= 0 && Offset <= 1020); + break; + } + + // If MI is not a debug value, make sure Offset fits in the 16-bit immediate + // field. + if (!MI.isDebugValue() && !Valid) { + MachineBasicBlock &MBB = *MI.getParent(); + DebugLoc DL = II->getDebugLoc(); + unsigned ADD = Xtensa::ADD; + unsigned Reg; + const XtensaInstrInfo &TII = *static_cast( + MBB.getParent()->getSubtarget().getInstrInfo()); + + TII.loadImmediate(MBB, II, &Reg, Offset); + BuildMI(MBB, II, DL, TII.get(ADD), Reg) + .addReg(FrameReg) + .addReg(Reg, RegState::Kill); + + FrameReg = Reg; + Offset = 0; + IsKill = true; + } + + MI.getOperand(OpNo).ChangeToRegister(FrameReg, false, false, IsKill); + MI.getOperand(OpNo + 1).ChangeToImmediate(Offset); +} + +void XtensaRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, + int SPAdj, unsigned FIOperandNum, + RegScavenger *RS) const { + MachineInstr &MI = *II; + MachineFunction &MF = *MI.getParent()->getParent(); + + LLVM_DEBUG(errs() << "\nFunction : " << MF.getName() << "\n"; + errs() << "<--------->\n" + << MI); + + int FrameIndex = MI.getOperand(FIOperandNum).getIndex(); + uint64_t stackSize = MF.getFrameInfo().getStackSize(); + int64_t spOffset = MF.getFrameInfo().getObjectOffset(FrameIndex); + + LLVM_DEBUG(errs() << "FrameIndex : " << FrameIndex << "\n" + << "spOffset : " << spOffset << "\n" + << "stackSize : " << stackSize << "\n"); + + eliminateFI(MI, FIOperandNum, FrameIndex, stackSize, spOffset); +} + +Register XtensaRegisterInfo::getFrameRegister(const MachineFunction &MF) const { + const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering(); + return TFI->hasFP(MF) ? (Subtarget.isWinABI() ? Xtensa::A7 : Xtensa::A15) + : Xtensa::SP; +} diff --git a/llvm/lib/Target/Xtensa/XtensaRegisterInfo.h b/llvm/lib/Target/Xtensa/XtensaRegisterInfo.h new file mode 100644 index 0000000000000..7f3506569d8c2 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaRegisterInfo.h @@ -0,0 +1,64 @@ +//===-- XtensaRegisterInfo.h - Xtensa Register Information Impl -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the Xtensa implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSAREGISTERINFO_H +#define LLVM_LIB_TARGET_XTENSA_XTENSAREGISTERINFO_H + +#include "Xtensa.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" + +#define GET_REGINFO_HEADER +#include "XtensaGenRegisterInfo.inc" + +namespace llvm { +class TargetRegisterClass; +class XtensaInstrInfo; +class XtensaSubtarget; + +struct XtensaRegisterInfo : public XtensaGenRegisterInfo { +public: + const XtensaSubtarget &Subtarget; + + XtensaRegisterInfo(const XtensaSubtarget &STI); + + bool requiresRegisterScavenging(const MachineFunction &MF) const override { + return true; + } + + bool requiresFrameIndexScavenging(const MachineFunction &MF) const override { + return true; + } + + bool trackLivenessAfterRegAlloc(const MachineFunction &) const override { + return true; + } + + const uint16_t * + getCalleeSavedRegs(const MachineFunction *MF = 0) const override; + const uint32_t *getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID) const override; + BitVector getReservedRegs(const MachineFunction &MF) const override; + void eliminateFrameIndex(MachineBasicBlock::iterator MI, int SPAdj, + unsigned FIOperandNum, + RegScavenger *RS) const override; + Register getFrameRegister(const MachineFunction &MF) const override; + +private: + void eliminateFI(MachineBasicBlock::iterator II, unsigned OpNo, + int FrameIndex, uint64_t StackSize, int64_t SPOffset) const; +}; + +} // end namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_REGISTERINFO_H */ diff --git a/llvm/lib/Target/Xtensa/XtensaRegisterInfo.td b/llvm/lib/Target/Xtensa/XtensaRegisterInfo.td new file mode 100644 index 0000000000000..93e67af82fc86 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaRegisterInfo.td @@ -0,0 +1,287 @@ +//===- XtensaRegisterInfo.td - Xtensa Register defs --------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Class definitions. +//===----------------------------------------------------------------------===// + +class XtensaReg : Register { + let Namespace = "Xtensa"; +} + +class XtensaRegWithSubRegs subregs> + : RegisterWithSubRegs { + let Namespace = "Xtensa"; +} + +//===----------------------------------------------------------------------===// +// General-purpose registers +//===----------------------------------------------------------------------===// + +// Xtensa general purpose regs +class ARReg num, string n, list alt = []> : XtensaReg { + let HWEncoding{3-0} = num; + let AltNames = alt; +} + +// Return Address +def A0 : ARReg<0, "a0">, DwarfRegNum<[0]>; + +// Stack Pointer (callee-saved) +def SP : ARReg<1, "a1", ["sp"]>, DwarfRegNum<[1]>; + +// Function Arguments +def A2 : ARReg<2, "a2">, DwarfRegNum<[2]>; +def A3 : ARReg<3, "a3">, DwarfRegNum<[3]>; +def A4 : ARReg<4, "a4">, DwarfRegNum<[4]>; +def A5 : ARReg<5, "a5">, DwarfRegNum<[5]>; +def A6 : ARReg<6, "a6">, DwarfRegNum<[6]>; +def A7 : ARReg<7, "a7">, DwarfRegNum<[7]>; + +// Static Chain +def A8 : ARReg<8, "a8">, DwarfRegNum<[8]>; + +def A9 : ARReg<9, "a9">, DwarfRegNum<[9]>; +def A10 : ARReg<10, "a10">, DwarfRegNum<[10]>; +def A11 : ARReg<11, "a11">, DwarfRegNum<[11]>; + +// Callee-saved +def A12 : ARReg<12, "a12">, DwarfRegNum<[12]>; +def A13 : ARReg<13, "a13">, DwarfRegNum<[13]>; +def A14 : ARReg<14, "a14">, DwarfRegNum<[14]>; + +// Stack-Frame Pointer (optional) - Callee-Saved +def A15 : ARReg<15, "a15">, DwarfRegNum<[15]>; + +// Register class with allocation order +def AR : RegisterClass<"Xtensa", [i32], 32, (add + A8, A9, A10, A11, A12, A13, A14, A15, + A7, A6, A5, A4, A3, A2, A0, SP)>; +//===----------------------------------------------------------------------===// +// Special-purpose registers +//===----------------------------------------------------------------------===// +class SRReg num, string n, list alt = []> : XtensaReg { + let HWEncoding{7-0} = num; + let AltNames = alt; +} + +def LBEG : SRReg<0, "lbeg", ["LBEG", "0"]>; +def LEND : SRReg<1, "lend", ["LEND", "1"]>; +def LCOUNT : SRReg<2, "lcount", ["LCOUNT", "2"]>; + +// Shift Amount Register +def SAR : SRReg<3, "sar", ["SAR","3"]>; + +def BREG : SRReg<4, "br", ["BR", "4"]>; +def LITBASE : SRReg<5, "litbase", ["LITBASE", "5"]>; + +// Expected data value for S32C1I operation +def SCOMPARE1 : SRReg<12, "scompare1", ["SCOMPARE1", "12"]>; + +def ACCLO : SRReg<16, "acclo", ["ACCLO", "16"]>; +def ACCHI : SRReg<17, "acchi", ["ACCHI", "17"]>; +def M0 : SRReg<32, "m0", ["M0", "32"]>; +def M1 : SRReg<33, "m1", ["M1", "33"]>; +def M2 : SRReg<34, "m2", ["M2", "34"]>; +def M3 : SRReg<35, "m3", ["M3", "35"]>; +def WINDOWBASE : SRReg<72, "windowbase", ["WINDOWBASE", "72"]>; +def WINDOWSTART : SRReg<73, "windowstart", ["WINDOWSTART", "73"]>; + +// Instuction breakpoint enable register +def IBREAKENABLE : SRReg<96, "ibreakenable", ["IBREAKENABLE", "96"]>; + +// Memory Control Register +def MEMCTL : SRReg<97, "memctl", ["MEMCTL", "97"]>; + +def ATOMCTL : SRReg<99, "atomctl", ["ATOMCTL", "99"]>; + +def DDR : SRReg<104, "ddr", ["DDR", "104"]>; + +// Instuction break address register 0 +def IBREAKA0 : SRReg<128, "ibreaka0", ["IBREAKA0", "128"]>; + +// Instuction break address register 1 +def IBREAKA1 : SRReg<129, "ibreaka1", ["IBREAKA1", "129"]>; + +// Data break address register 0 +def DBREAKA0 : SRReg<144, "dbreaka0", ["DBREAKA0", "144"]>; + +// Data break address register 1 +def DBREAKA1 : SRReg<145, "dbreaka1", ["DBREAKA1", "145"]>; + +// Data breakpoint control register 0 +def DBREAKC0 : SRReg<160, "dbreakc0", ["DBREAKC0", "160"]>; + +// Data breakpoint control register 1 +def DBREAKC1 : SRReg<161, "dbreakc1", ["DBREAKC1", "161"]>; + +def CONFIGID0 : SRReg<176, "configid0", ["CONFIGID0", "176"]>; + +// Exception PC1 +def EPC1 : SRReg<177, "epc1", ["EPC1", "177"]>; + +// Exception PC2 +def EPC2 : SRReg<178, "epc2", ["EPC2", "178"]>; + +// Exception PC3 +def EPC3 : SRReg<179, "epc3", ["EPC3", "179"]>; + +// Exception PC4 +def EPC4 : SRReg<180, "epc4", ["EPC4", "180"]>; + +// Exception PC5 +def EPC5 : SRReg<181, "epc5", ["EPC5", "181"]>; + +// Exception PC6 +def EPC6 : SRReg<182, "epc6", ["EPC6", "182"]>; + +// Exception PC7 +def EPC7 : SRReg<183, "epc7", ["EPC7", "183"]>; + +def DEPC : SRReg<192, "depc", ["DEPC", "192"]>; +def EPS2 : SRReg<194, "eps2", ["EPS2", "194"]>; +def EPS3 : SRReg<195, "eps3", ["EPS3", "195"]>; +def EPS4 : SRReg<196, "eps4", ["EPS4", "196"]>; +def EPS5 : SRReg<197, "eps5", ["EPS5", "197"]>; +def EPS6 : SRReg<198, "eps6", ["EPS6", "198"]>; +def EPS7 : SRReg<199, "eps7", ["EPS7", "199"]>; +def CONFIGID1 : SRReg<208, "configid1", ["CONFIGID1", "208"]>; +def EXCSAVE1 : SRReg<209, "excsave1", ["EXCSAVE1", "209"]>; +def EXCSAVE2 : SRReg<210, "excsave2", ["EXCSAVE2", "210"]>; +def EXCSAVE3 : SRReg<211, "excsave3", ["EXCSAVE3", "211"]>; +def EXCSAVE4 : SRReg<212, "excsave4", ["EXCSAVE4", "212"]>; +def EXCSAVE5 : SRReg<213, "excsave5", ["EXCSAVE5", "213"]>; +def EXCSAVE6 : SRReg<214, "excsave6", ["EXCSAVE6", "214"]>; +def EXCSAVE7 : SRReg<215, "excsave7", ["EXCSAVE7", "215"]>; +def CPENABLE : SRReg<224, "cpenable", ["CPENABLE", "224"]>; + +// Interrupt enable mask register +def INTSET : SRReg<226, "interrupt", ["INTERRUPT", "226"]>; + +def INTCLEAR : SRReg<227, "intclear", ["INTCLEAR", "227"]>; + +def INTENABLE : SRReg<228, "intenable", ["INTENABLE", "228"]>; + +// Processor State +def PS : SRReg<230, "ps", ["PS", "230"]>; + +// Vector base register +def VECBASE : SRReg<231, "vecbase", ["VECBASE", "231"]>; + +def EXCCAUSE : SRReg<232, "exccause", ["EXCCAUSE", "232"]>; + +// Cause of last debug exception register +def DEBUGCAUSE : SRReg<233, "debugcause", ["DEBUGCAUSE", "233"]>; + +// Processor Clock Count Register +def CCOUNT : SRReg<234, "ccount", ["CCOUNT", "234"]>; + +// Processor ID Register +def PRID : SRReg<235, "prid", ["PRID", "235"]>; + +def ICOUNT : SRReg<236, "icount", ["ICOUNT", "236"]>; +def ICOUNTLEVEL : SRReg<237, "icountlevel", ["ICOUNTLEVEL", "237"]>; +def EXCVADDR : SRReg<238, "excvaddr", ["EXCVADDR", "238"]>; + +// Cycle number to interrupt register 0 +def CCOMPARE0 : SRReg<240, "ccompare0", ["CCOMPARE0", "240"]>; + +// Cycle number to interrupt register 1 +def CCOMPARE1 : SRReg<241, "ccompare1", ["CCOMPARE1", "241"]>; + +// Cycle number to interrupt register 2 +def CCOMPARE2 : SRReg<242, "ccompare2", ["CCOMPARE2", "242"]>; + +def MISC0 : SRReg<244, "misc0", ["MISC0", "244"]>; +def MISC1 : SRReg<245, "misc1", ["MISC1", "245"]>; +def MISC2 : SRReg<246, "misc2", ["MISC2", "246"]>; +def MISC3 : SRReg<247, "misc3", ["MISC3", "247"]>; + +def MR01 : RegisterClass<"Xtensa", [i32], 32, (add M0, M1)>; +def MR23 : RegisterClass<"Xtensa", [i32], 32, (add M2, M3)>; +def MR : RegisterClass<"Xtensa", [i32], 32, (add MR01, MR23)>; + +def SR : RegisterClass<"Xtensa", [i32], 32, (add + LBEG, LEND, LCOUNT, SAR, BREG, LITBASE, SCOMPARE1, ACCLO, ACCHI, MR, + WINDOWBASE, WINDOWSTART, IBREAKENABLE, MEMCTL, ATOMCTL, DDR, IBREAKA0, IBREAKA1, + DBREAKA0, DBREAKA1, DBREAKC0, DBREAKC1, CONFIGID0, EPC1, EPC2, EPC3, EPC4, EPC5, + EPC6, EPC7, DEPC, EPS2, EPS3, EPS4, EPS5, EPS6, EPS7, CONFIGID1, EXCSAVE1, EXCSAVE2, + EXCSAVE3, EXCSAVE4, EXCSAVE5, EXCSAVE6, EXCSAVE7, CPENABLE, INTSET, INTCLEAR, INTENABLE, PS, + VECBASE, EXCCAUSE, DEBUGCAUSE, CCOUNT, PRID, ICOUNT, ICOUNTLEVEL, EXCVADDR, CCOMPARE0, + CCOMPARE1, CCOMPARE2, MISC0, MISC1, MISC2, MISC3)>; + +//===----------------------------------------------------------------------===// +// USER registers +//===----------------------------------------------------------------------===// +class URReg num, string n, list alt = []> : XtensaReg { + let HWEncoding{7-0} = num; + let AltNames = alt; +} + +def GPIO_OUT : URReg<0, "gpio_out", ["GPIO_OUT"]>; +def EXPSTATE : URReg<230, "expstate", ["EXPSTATE"]>; + +// Thread Pointer register +def THREADPTR : URReg<231, "threadptr", ["THREADPTR"]>; + +def FCR : URReg<232, "fcr", ["FCR"]>; +def FSR : URReg<233, "fsr", ["FSR"]>; +def F64R_LO : URReg<234, "f64r_lo", ["F64R_LO"]>; +def F64R_HI : URReg<235, "f64r_hi", ["F64R_HI"]>; +def F64S : URReg<236, "f64s", ["F64S"]>; + +def UR : RegisterClass<"Xtensa", [i32], 32, (add GPIO_OUT, EXPSTATE, THREADPTR, FCR, + FSR, F64R_LO, F64R_HI, F64S)>; + +//===----------------------------------------------------------------------===// +// Floating-Point registers +//===----------------------------------------------------------------------===// + +// Xtensa Floating-Point regs +class FPReg num, string n> : XtensaReg { + let HWEncoding{3-0} = num; +} + +def F0 : FPReg<0, "f0">, DwarfRegNum<[19]>; +def F1 : FPReg<1, "f1">, DwarfRegNum<[20]>; +def F2 : FPReg<2, "f2">, DwarfRegNum<[21]>; +def F3 : FPReg<3, "f3">, DwarfRegNum<[22]>; +def F4 : FPReg<4, "f4">, DwarfRegNum<[23]>; +def F5 : FPReg<5, "f5">, DwarfRegNum<[24]>; +def F6 : FPReg<6, "f6">, DwarfRegNum<[25]>; +def F7 : FPReg<7, "f7">, DwarfRegNum<[26]>; +def F8 : FPReg<8, "f8">, DwarfRegNum<[27]>; +def F9 : FPReg<9, "f9">, DwarfRegNum<[28]>; +def F10 : FPReg<10, "f10">, DwarfRegNum<[29]>; +def F11 : FPReg<11, "f11">, DwarfRegNum<[30]>; +def F12 : FPReg<12, "f12">, DwarfRegNum<[31]>; +def F13 : FPReg<13, "f13">, DwarfRegNum<[32]>; +def F14 : FPReg<14, "f14">, DwarfRegNum<[33]>; +def F15 : FPReg<15, "f15">, DwarfRegNum<[34]>; + +// Floating-Point register class with allocation order +def FPR : RegisterClass<"Xtensa", [f32], 32, (add + F8, F9, F10, F11, F12, F13, F14, F15, + F7, F6, F5, F4, F3, F2, F1, F0)>; + +//===----------------------------------------------------------------------===// +// Boolean registers +//===----------------------------------------------------------------------===// +class BReg num, string n> : XtensaReg { + let HWEncoding{3-0} = num; +} + +foreach i = 0-15 in { + def B#i : BReg, DwarfRegNum<[i]>; +} + +// Boolean register class +def BR : RegisterClass<"Xtensa", [i1], 0, (add B0, B1, +B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15)>; diff --git a/llvm/lib/Target/Xtensa/XtensaSizeReductionPass.cpp b/llvm/lib/Target/Xtensa/XtensaSizeReductionPass.cpp new file mode 100644 index 0000000000000..1377d7fccf1e0 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaSizeReductionPass.cpp @@ -0,0 +1,266 @@ +//===- XtensaSizeReductionPass.cpp - Xtensa Size Reduction ----------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Xtensa.h" +#include "XtensaInstrInfo.h" +#include "XtensaSubtarget.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen//MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Target/TargetMachine.h" + +using namespace llvm; + +#define DEBUG_TYPE "xtensa-size-reduce-pass" + +STATISTIC(NumReduced, "Number of 24-bit instructions reduced to 16-bit ones"); + +class XtensaSizeReduce : public MachineFunctionPass { +public: + static char ID; + XtensaSizeReduce() : MachineFunctionPass(ID) {} + + const XtensaSubtarget *Subtarget; + static const XtensaInstrInfo *XtensaII; + + bool runOnMachineFunction(MachineFunction &MF) override; + + llvm::StringRef getPassName() const override { + return "Xtensa instruction size reduction pass"; + } + +private: + /// Reduces width of instructions in the specified basic block. + bool ReduceMBB(MachineBasicBlock &MBB); + + /// Attempts to reduce MI, returns true on success. + bool ReduceMI(const MachineBasicBlock::instr_iterator &MII); +}; + +char XtensaSizeReduce::ID = 0; +const XtensaInstrInfo *XtensaSizeReduce::XtensaII; + +bool XtensaSizeReduce::ReduceMI(const MachineBasicBlock::instr_iterator &MII) { + MachineInstr *MI = &*MII; + MachineBasicBlock &MBB = *MI->getParent(); + unsigned Opcode = MI->getOpcode(); + + switch (Opcode) { + case Xtensa::L32I: { + MachineOperand Op0 = MI->getOperand(0); + MachineOperand Op1 = MI->getOperand(1); + MachineOperand Op2 = MI->getOperand(2); + + int64_t Imm = Op2.getImm(); + if (Imm >= 0 && Imm <= 60) { + // Replace L32I to L32I.N + DebugLoc dl = MI->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::L32I_N); + MachineInstrBuilder MIB = BuildMI(MBB, MI, dl, NewMCID); + MIB.add(Op0); + MIB.add(Op1); + MIB.add(Op2); + // Transfer MI flags. + MIB.setMIFlags(MI->getFlags()); + LLVM_DEBUG(dbgs() << " to 16-bit: " << *MIB); + NumReduced++; + MBB.erase_instr(MI); + return true; + } + } break; + + case Xtensa::S32I: { + MachineOperand Op0 = MI->getOperand(0); + MachineOperand Op1 = MI->getOperand(1); + MachineOperand Op2 = MI->getOperand(2); + + int64_t Imm = Op2.getImm(); + if (Imm >= 0 && Imm <= 60) { + // Replace S32I to S32I.N + DebugLoc dl = MI->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::S32I_N); + MachineInstrBuilder MIB = BuildMI(MBB, MI, dl, NewMCID); + MIB.add(Op0); + MIB.add(Op1); + MIB.add(Op2); + // Transfer MI flags. + MIB.setMIFlags(MI->getFlags()); + LLVM_DEBUG(dbgs() << " to 16-bit: " << *MIB); + NumReduced++; + MBB.erase_instr(MI); + return true; + } + + } break; + + case Xtensa::MOVI: { + MachineOperand Op0 = MI->getOperand(0); + MachineOperand Op1 = MI->getOperand(1); + + int64_t Imm = Op1.getImm(); + if (Imm >= -32 && Imm <= 95) { + // Replace MOVI to MOVI.N + DebugLoc dl = MI->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::MOVI_N); + MachineInstrBuilder MIB = BuildMI(MBB, MI, dl, NewMCID); + MIB.add(Op0); + MIB.add(Op1); + // Transfer MI flags. + MIB.setMIFlags(MI->getFlags()); + LLVM_DEBUG(dbgs() << " to 16-bit: " << *MIB); + NumReduced++; + MBB.erase_instr(MI); + return true; + } + + } break; + + case Xtensa::ADD: { + MachineOperand Op0 = MI->getOperand(0); + MachineOperand Op1 = MI->getOperand(1); + MachineOperand Op2 = MI->getOperand(2); + + // Replace ADD to ADD.N + DebugLoc dl = MI->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::ADD_N); + MachineInstrBuilder MIB = BuildMI(MBB, MI, dl, NewMCID); + MIB.add(Op0); + MIB.add(Op1); + MIB.add(Op2); + // Transfer MI flags. + MIB.setMIFlags(MI->getFlags()); + LLVM_DEBUG(dbgs() << " to 16-bit: " << *MIB); + NumReduced++; + MBB.erase_instr(MI); + return true; + + } break; + + case Xtensa::ADDI: { + MachineOperand Op0 = MI->getOperand(0); + MachineOperand Op1 = MI->getOperand(1); + MachineOperand Op2 = MI->getOperand(2); + + int64_t Imm = Op2.getImm(); + if ((Imm >= 1 && Imm <= 15) || (Imm == -1)) { + // Replace ADDI to ADDI.N + DebugLoc dl = MI->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::ADDI_N); + MachineInstrBuilder MIB = BuildMI(MBB, MI, dl, NewMCID); + MIB.add(Op0); + MIB.add(Op1); + MIB.add(Op2); + // Transfer MI flags. + MIB.setMIFlags(MI->getFlags()); + LLVM_DEBUG(dbgs() << " to 16-bit: " << *MIB); + NumReduced++; + MBB.erase_instr(MI); + return true; + } + } break; + + case Xtensa::OR: { + MachineOperand Op0 = MI->getOperand(0); + MachineOperand Op1 = MI->getOperand(1); + MachineOperand Op2 = MI->getOperand(2); + + if (Op1.getReg() != Op2.getReg()) + break; + + // Replace OR R1, R2, R2 to MOV.N R1, R2 + DebugLoc dl = MI->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::MOV_N); + MachineInstrBuilder MIB = BuildMI(MBB, MI, dl, NewMCID); + MIB.add(Op0); + MIB.add(Op1); + // Transfer MI flags. + MIB.setMIFlags(MI->getFlags()); + LLVM_DEBUG(dbgs() << " to 16-bit: " << *MIB); + NumReduced++; + MBB.erase_instr(MI); + return true; + } break; + + case Xtensa::RET: { + // Replace RET to RET.N + DebugLoc dl = MI->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::RET_N); + MachineInstrBuilder MIB = BuildMI(MBB, MI, dl, NewMCID); + // Transfer MI flags. + MIB.setMIFlags(MI->getFlags()); + LLVM_DEBUG(dbgs() << " to 16-bit: " << *MIB); + NumReduced++; + MBB.erase_instr(MI); + return true; + } break; + + case Xtensa::RETW: { + // Replace RETW to RETW.N + DebugLoc dl = MI->getDebugLoc(); + const MCInstrDesc &NewMCID = XtensaII->get(Xtensa::RETW_N); + MachineInstrBuilder MIB = BuildMI(MBB, MI, dl, NewMCID); + // Transfer MI flags. + MIB.setMIFlags(MI->getFlags()); + LLVM_DEBUG(dbgs() << " to 16-bit: " << *MIB); + NumReduced++; + MBB.erase_instr(MI); + return true; + } break; + + default: + break; + } + + return false; +} + +bool XtensaSizeReduce::ReduceMBB(MachineBasicBlock &MBB) { + bool Modified = false; + MachineBasicBlock::instr_iterator MII = MBB.instr_begin(), + E = MBB.instr_end(); + MachineBasicBlock::instr_iterator NextMII; + + // Iterate through the instructions in the basic block + for (; MII != E; MII = NextMII) { + NextMII = std::next(MII); + MachineInstr *MI = &*MII; + + // Don't reduce bundled instructions or pseudo operations + if (MI->isBundle() || MI->isTransient()) + continue; + + // Try to reduce 24-bit instruction into 16-bit instruction + Modified |= ReduceMI(MII); + } + + return Modified; +} + +bool XtensaSizeReduce::runOnMachineFunction(MachineFunction &MF) { + + Subtarget = &static_cast(MF.getSubtarget()); + XtensaII = static_cast(Subtarget->getInstrInfo()); + bool Modified = false; + + if (!Subtarget->hasDensity()) + return Modified; + + MachineFunction::iterator I = MF.begin(), E = MF.end(); + + for (; I != E; ++I) + Modified |= ReduceMBB(*I); + return Modified; +} + +FunctionPass *llvm::createXtensaSizeReductionPass() { + return new XtensaSizeReduce(); +} diff --git a/llvm/lib/Target/Xtensa/XtensaSubtarget.cpp b/llvm/lib/Target/Xtensa/XtensaSubtarget.cpp new file mode 100644 index 0000000000000..311b757d55be1 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaSubtarget.cpp @@ -0,0 +1,88 @@ +//===- XtensaSubtarget.cpp - Xtensa Subtarget Information -----------------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the Xtensa specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#include "XtensaSubtarget.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "xtensa-subtarget" + +#define GET_SUBTARGETINFO_TARGET_DESC +#define GET_SUBTARGETINFO_CTOR +#include "XtensaGenSubtargetInfo.inc" + +using namespace llvm; + +static cl::opt TextSectionLiterals("mtext-section-literals", + cl::init(false), cl::Hidden); + +bool XtensaSubtarget::useTextSectionLiterals() const +{ + // If code model is large then always place literals in + // test section. + if (TLInfo.getTargetMachine().getCodeModel() == CodeModel::Large) + return true; + + return TextSectionLiterals; +} + +XtensaSubtarget & +XtensaSubtarget::initializeSubtargetDependencies(StringRef CPU, StringRef FS) { + StringRef CPUName = CPU; + if (CPUName.empty()) { + // set default cpu name + CPUName = "esp32"; + } + + HasDensity = false; + HasSingleFloat = false; + HasWindowed = false; + HasBoolean = false; + HasLoop = false; + HasSEXT = false; + HasNSA = false; + HasMul16 = false; + HasMul32 = false; + HasMul32High = false; + HasDiv32 = false; + HasMAC16 = false; + HasDFPAccel = false; + HasS32C1I = false; + HasTHREADPTR = false; + HasExtendedL32R = false; + HasATOMCTL = false; + HasMEMCTL = false; + HasDebug = false; + HasException = false; + HasHighPriInterrupts = false; + HasCoprocessor = false; + HasInterrupt = false; + HasRelocatableVector = false; + HasTimerInt = false; + HasPRID = false; + HasRegionProtection = false; + HasMiscSR = false; + HasESP32S2Ops = false; + HasESP32S3Ops = false; + + // Parse features string. + ParseSubtargetFeatures(CPUName, CPUName, FS); + return *this; +} + +XtensaSubtarget::XtensaSubtarget(const Triple &TT, const std::string &CPU, + const std::string &FS, const TargetMachine &TM) + : XtensaGenSubtargetInfo(TT, CPU, /*TuneCPU*/ CPU, FS), TargetTriple(TT), + InstrInfo(initializeSubtargetDependencies(CPU, FS)), TLInfo(TM, *this), + TSInfo(), FrameLowering() {} diff --git a/llvm/lib/Target/Xtensa/XtensaSubtarget.h b/llvm/lib/Target/Xtensa/XtensaSubtarget.h new file mode 100644 index 0000000000000..a6647b5209613 --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaSubtarget.h @@ -0,0 +1,215 @@ +//===-- XtensaSubtarget.h - Define Subtarget for the Xtensa ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the Xtensa specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSASUBTARGET_H +#define LLVM_LIB_TARGET_XTENSA_XTENSASUBTARGET_H + +#include "XtensaFrameLowering.h" +#include "XtensaISelLowering.h" +#include "XtensaInstrInfo.h" +#include "XtensaRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAGTargetInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/Target/TargetMachine.h" + +#define GET_SUBTARGETINFO_HEADER +#include "XtensaGenSubtargetInfo.inc" + +namespace llvm { +class StringRef; + +class XtensaSubtarget : public XtensaGenSubtargetInfo { +private: + Triple TargetTriple; + XtensaInstrInfo InstrInfo; + XtensaTargetLowering TLInfo; + SelectionDAGTargetInfo TSInfo; + XtensaFrameLowering FrameLowering; + + // Enabled Xtensa Density extension + bool HasDensity; + + // Enabled Xtensa Single FP instructions + bool HasSingleFloat; + + // Enabled Xtensa Windowed Register option + bool HasWindowed; + + // Enabled Xtensa Boolean extension + bool HasBoolean; + + // Enabled Xtensa Loop extension + bool HasLoop; + + // Enable Xtensa Sign Extend option + bool HasSEXT; + + // Enable Xtensa NSA option + bool HasNSA; + + // Enable Xtensa Mul16 option + bool HasMul16; + + // Enable Xtensa Mul32 option + bool HasMul32; + + // Enable Xtensa Mul32High option + bool HasMul32High; + + // Enable Xtensa Div32 option + bool HasDiv32; + + // Enabled Xtensa MAC16 instructions + bool HasMAC16; + + // Enable Xtensa Xtensa Double Precision FP acceleration + bool HasDFPAccel; + + // Enable Xtensa S32C1I option + bool HasS32C1I; + + // Enable Xtensa THREADPTR option + bool HasTHREADPTR; + + // Enable Xtensa Extended L32R option + bool HasExtendedL32R; + + // Enable Xtensa ATOMCTL option + bool HasATOMCTL; + + // Enable Xtensa ATOMCTL option + bool HasMEMCTL; + + // Enable Xtensa Debug option + bool HasDebug; + + // Enable Xtensa Exceptions option + bool HasException; + + // Enable Xtensa High Priority Interrupt option + bool HasHighPriInterrupts; + + // Enable Xtensa Coprocessor option + bool HasCoprocessor; + + // Enable Xtensa Interrupt option + bool HasInterrupt; + + // Enable Xtensa Relocatable Vector option + bool HasRelocatableVector; + + // Enable Xtensa Timer Interrupt option + bool HasTimerInt; + + // Enable Xtensa Processor ID option + bool HasPRID; + + // Enable Xtensa Region Protection option + bool HasRegionProtection; + + // Enable Xtensa Miscellaneous Special Reigsiters option + bool HasMiscSR; + + // Enable Xtensa esp32-s2 ISA extension + bool HasESP32S2Ops; + + // Enable Xtensa esp32-s3 ISA extension + bool HasESP32S3Ops; + + XtensaSubtarget &initializeSubtargetDependencies(StringRef CPU, StringRef FS); + +public: + XtensaSubtarget(const Triple &TT, const std::string &CPU, + const std::string &FS, const TargetMachine &TM); + + const TargetFrameLowering *getFrameLowering() const override { return &FrameLowering; } + const XtensaInstrInfo *getInstrInfo() const override { return &InstrInfo; } + const XtensaRegisterInfo *getRegisterInfo() const override { + return &InstrInfo.getRegisterInfo(); + } + + const XtensaTargetLowering *getTargetLowering() const override { return &TLInfo; } + const SelectionDAGTargetInfo *getSelectionDAGInfo() const override { return &TSInfo; } + + bool isWinABI() const { return hasWindowed(); } + + bool hasDensity() const { return HasDensity; } + + bool hasSingleFloat() const { return HasSingleFloat; } + + bool hasWindowed() const { return HasWindowed; } + + bool hasBoolean() const { return HasBoolean; } + + bool hasLoop() const { return HasLoop; } + + bool hasSEXT() const { return HasSEXT; } + + bool hasNSA() const { return HasNSA; } + + bool hasMul16() const { return HasMul16; } + + bool hasMul32() const { return HasMul32; } + + bool hasMul32High() const { return HasMul32High; } + + bool hasDiv32() const { return HasDiv32; } + + bool hasMAC16() const { return HasMAC16; } + + bool hasDFPAccel() const { return HasDFPAccel; } + + bool hasS32C1I() const { return HasS32C1I; } + + bool hasTHREADPTR() const { return HasTHREADPTR; } + + bool hasExtendedL32R() const { return HasExtendedL32R; } + + bool hasATOMCTL() const { return HasATOMCTL; } + + bool hasMEMCTL() const { return HasMEMCTL; } + + bool hasDebug() const { return HasDebug; } + + bool hasException() const { return HasException; } + + bool hasHighPriInterrupts() const { return HasHighPriInterrupts; } + + bool hasCoprocessor() const { return HasCoprocessor; } + + bool hasInterrupt() const { return HasInterrupt; } + + bool hasRelocatableVector() const { return HasRelocatableVector; } + + bool hasTimerInt() const { return HasTimerInt; } + + bool hasPRID() const { return HasPRID; } + + bool hasRegionProtection() const { return HasRegionProtection; } + + bool hasMiscSR() const { return HasMiscSR; } + + bool hasESP32S2Ops() const { return HasESP32S2Ops; } + + bool hasESP32S3Ops() const { return HasESP32S3Ops; } + + bool useTextSectionLiterals() const; + + // Automatically generated by tblgen. + void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS); +}; +} // end namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_XTENSASUBTARGET_H */ diff --git a/llvm/lib/Target/Xtensa/XtensaTargetMachine.cpp b/llvm/lib/Target/Xtensa/XtensaTargetMachine.cpp new file mode 100644 index 0000000000000..dd8003a429f5d --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaTargetMachine.cpp @@ -0,0 +1,144 @@ +//===- XtensaTargetMachine.cpp - Define TargetMachine for Xtensa ----------===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements the info about Xtensa target spec. +// +//===----------------------------------------------------------------------===// + +#include "XtensaTargetMachine.h" +#include "XtensaTargetObjectFile.h" +#include "XtensaTargetTransformInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Scalar.h" + +using namespace llvm; + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeXtensaTarget() { + // Register the target. + RegisterTargetMachine A(TheXtensaTarget); +} + +static std::string computeDataLayout(const Triple &TT, StringRef CPU, + const TargetOptions &Options, + bool IsLittle) { + std::string Ret = "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32"; + + return Ret; +} + +static Reloc::Model getEffectiveRelocModel(bool JIT, + Optional RM) { + if (!RM.hasValue() || JIT) + return Reloc::Static; + return *RM; +} + +static std::unique_ptr createTLOF() { + return std::make_unique(); +} + +static StringRef getCPUName(StringRef CPU) { + if (CPU.empty()) + CPU = "esp32"; + else if (CPU == "esp32-s2") + CPU = "esp32s2"; + else if (CPU == "esp32-s3") + CPU = "esp32s3"; + return CPU; +} + +XtensaTargetMachine::XtensaTargetMachine(const Target &T, const Triple &TT, + StringRef CPU, StringRef FS, + const TargetOptions &Options, + Optional RM, + Optional CM, + CodeGenOpt::Level OL, bool JIT, + bool IsLittle) + : LLVMTargetMachine(T, computeDataLayout(TT, CPU, Options, IsLittle), TT, + CPU, FS, Options, getEffectiveRelocModel(JIT, RM), + getEffectiveCodeModel(CM, CodeModel::Small), OL), + TLOF(createTLOF()), + Subtarget(TT, std::string(CPU), std::string(FS), *this) { + initAsmInfo(); +} + +XtensaTargetMachine::XtensaTargetMachine(const Target &T, const Triple &TT, + StringRef CPU, StringRef FS, + const TargetOptions &Options, + Optional RM, + Optional CM, + CodeGenOpt::Level OL, bool JIT) + : XtensaTargetMachine(T, TT, getCPUName(CPU), FS, Options, RM, CM, OL, JIT, true) {} + +const XtensaSubtarget * +XtensaTargetMachine::getSubtargetImpl(const Function &F) const { + return &Subtarget; +} + +TargetTransformInfo +XtensaTargetMachine::getTargetTransformInfo(const Function &F) const { + return TargetTransformInfo(XtensaTTIImpl(this, F)); +} + +namespace { +/// Xtensa Code Generator Pass Configuration Options. +class XtensaPassConfig : public TargetPassConfig { +public: + XtensaPassConfig(XtensaTargetMachine &TM, PassManagerBase &PM) + : TargetPassConfig(TM, PM) {} + + XtensaTargetMachine &getXtensaTargetMachine() const { + return getTM(); + } + + void addIRPasses() override; + bool addPreISel() override; + bool addInstSelector() override; + void addPreRegAlloc() override; + void addPreEmitPass() override; +}; +} // end anonymous namespace + +bool XtensaPassConfig::addPreISel() { + if (TM->getOptLevel() != CodeGenOpt::None) { + addPass(createHardwareLoopsPass()); + } + + return false; +} + +bool XtensaPassConfig::addInstSelector() { + addPass(createXtensaISelDag(getXtensaTargetMachine(), getOptLevel())); + return false; +} + +void XtensaPassConfig::addIRPasses() { addPass(createAtomicExpandPass()); } + +void XtensaPassConfig::addPreRegAlloc() { + addPass(createXtensaHardwareLoops()); +} + +void XtensaPassConfig::addPreEmitPass() { + addPass(createXtensaPSRAMCacheFixPass()); + addPass(createXtensaSizeReductionPass()); + addPass(createXtensaFixupHwLoops()); + addPass(&BranchRelaxationPassID); + addPass(createXtensaConstantIslandPass()); +} + +TargetPassConfig *XtensaTargetMachine::createPassConfig(PassManagerBase &PM) { + return new XtensaPassConfig(*this, PM); +} + diff --git a/llvm/lib/Target/Xtensa/XtensaTargetMachine.h b/llvm/lib/Target/Xtensa/XtensaTargetMachine.h new file mode 100644 index 0000000000000..bd4948b5ea56c --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaTargetMachine.h @@ -0,0 +1,58 @@ +//===-- XtensaTargetMachine.h - Define TargetMachine for Xtensa -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the Xtensa specific subclass of TargetMachine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSATARGETMACHINE_H +#define LLVM_LIB_TARGET_XTENSA_XTENSATARGETMACHINE_H + +#include "XtensaSubtarget.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { + +class TargetFrameLowering; + +extern Target TheXtensaTarget; + +class XtensaTargetMachine : public LLVMTargetMachine { + std::unique_ptr TLOF; + +public: + XtensaTargetMachine(const Target &T, const Triple &TT, StringRef CPU, + StringRef FS, const TargetOptions &Options, + Optional RM, Optional CM, + CodeGenOpt::Level OL, bool JIT, bool isLittle); + + XtensaTargetMachine(const Target &T, const Triple &TT, StringRef CPU, + StringRef FS, const TargetOptions &Options, + Optional RM, Optional CM, + CodeGenOpt::Level OL, bool JIT); + + TargetTransformInfo getTargetTransformInfo(const Function &F) const override; + + // Override TargetMachine. + const XtensaSubtarget *getSubtargetImpl() const { return &Subtarget; } + const XtensaSubtarget *getSubtargetImpl(const Function &F) const override; + // Override LLVMTargetMachine + TargetPassConfig *createPassConfig(PassManagerBase &PM) override; + TargetLoweringObjectFile *getObjFileLowering() const override { + return TLOF.get(); + } + +protected: + XtensaSubtarget Subtarget; +}; +} // end namespace llvm + +#endif /* LLVM_LIB_TARGET_XTENSA_XTENSATARGETMACHINE_H */ + diff --git a/llvm/lib/Target/Xtensa/XtensaTargetObjectFile.cpp b/llvm/lib/Target/Xtensa/XtensaTargetObjectFile.cpp new file mode 100644 index 0000000000000..27da879ea860d --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaTargetObjectFile.cpp @@ -0,0 +1,23 @@ +//===-- llvm/Target/XtensaTargetObjectFile.cpp - Xtensa Object Info Impl --===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "XtensaTargetObjectFile.h" +#include "llvm/MC/MCContext.h" +#include "llvm/Target/TargetMachine.h" + +using namespace llvm; + +//===----------------------------------------------------------------------===// +// ELF Target +//===----------------------------------------------------------------------===// + +void XtensaElfTargetObjectFile::Initialize(MCContext &Ctx, + const TargetMachine &TM) { + TargetLoweringObjectFileELF::Initialize(Ctx, TM); + InitializeELF(false); +} diff --git a/llvm/lib/Target/Xtensa/XtensaTargetObjectFile.h b/llvm/lib/Target/Xtensa/XtensaTargetObjectFile.h new file mode 100644 index 0000000000000..dae8f890459aa --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaTargetObjectFile.h @@ -0,0 +1,25 @@ +//===- llvm/Target/XtensaTargetObjectFile.h - Xtensa Object Info -*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSATARGETOBJECTFILE_H +#define LLVM_LIB_TARGET_XTENSA_XTENSATARGETOBJECTFILE_H + +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" + +namespace llvm { + +class XtensaElfTargetObjectFile : public TargetLoweringObjectFileELF { +public: + XtensaElfTargetObjectFile() : TargetLoweringObjectFileELF() {} + + void Initialize(MCContext &Ctx, const TargetMachine &TM) override; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_XTENSA_XTENSATARGETOBJECTFILE_H diff --git a/llvm/lib/Target/Xtensa/XtensaTargetTransformInfo.cpp b/llvm/lib/Target/Xtensa/XtensaTargetTransformInfo.cpp new file mode 100644 index 0000000000000..4accacb20f2ad --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaTargetTransformInfo.cpp @@ -0,0 +1,41 @@ +//===- XtensaTargetTransformInfo.cpp - Xtensa specific TTI ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "XtensaTargetTransformInfo.h" + +using namespace llvm; + +#define DEBUG_TYPE "xtensatti" + +static cl::opt DisableLowOverheadLoops( + "disable-xtensa-hwloops", cl::Hidden, cl::init(true), + cl::desc("Disable the generation of hardware loops")); + +bool XtensaTTIImpl::isHardwareLoopProfitable(Loop *L, ScalarEvolution &SE, + AssumptionCache &AC, + TargetLibraryInfo *LibInfo, + HardwareLoopInfo &HWLoopInfo) { + // Disable hw loops when literals are placed in text section. + // TODO: Implement support of hw loops in ConstantIslands pass + if (ST->useTextSectionLiterals()) { + return false; + } + + if (DisableLowOverheadLoops) + return false; + + if (!ST->hasLoop()) + return false; + + LLVMContext &C = L->getHeader()->getContext(); + HWLoopInfo.CounterInReg = true; + HWLoopInfo.IsNestingLegal = false; + HWLoopInfo.CountType = Type::getInt32Ty(C); + HWLoopInfo.LoopDecrement = ConstantInt::get(HWLoopInfo.CountType, 1); + return true; +} diff --git a/llvm/lib/Target/Xtensa/XtensaTargetTransformInfo.h b/llvm/lib/Target/Xtensa/XtensaTargetTransformInfo.h new file mode 100644 index 0000000000000..81bfbacc0381e --- /dev/null +++ b/llvm/lib/Target/Xtensa/XtensaTargetTransformInfo.h @@ -0,0 +1,51 @@ +//===- XtensaTargetTransformInfo.h - Xtensa specific TTI --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file defines a TargetTransformInfo::Concept conforming object specific +/// to the Xtensa target machine. It uses the target's detailed information to +/// provide more precise answers to certain TTI queries, while letting the +/// target independent and default TTI implementations handle the rest. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_XTENSA_XTENSATARGETTRANSFORMINFO_H +#define LLVM_LIB_TARGET_XTENSA_XTENSATARGETTRANSFORMINFO_H + +#include "XtensaSubtarget.h" +#include "XtensaTargetMachine.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/BasicTTIImpl.h" +#include "llvm/IR/Function.h" + +namespace llvm { + +class XtensaTTIImpl : public BasicTTIImplBase { + using BaseT = BasicTTIImplBase; + using TTI = TargetTransformInfo; + + friend BaseT; + + const XtensaSubtarget *ST; + const XtensaTargetLowering *TLI; + + const XtensaSubtarget *getST() const { return ST; } + const XtensaTargetLowering *getTLI() const { return TLI; } + +public: + explicit XtensaTTIImpl(const XtensaTargetMachine *TM, const Function &F) + : BaseT(TM, F.getParent()->getDataLayout()), ST(TM->getSubtargetImpl(F)), + TLI(ST->getTargetLowering()) {} + + bool isHardwareLoopProfitable(Loop *L, ScalarEvolution &SE, + AssumptionCache &AC, TargetLibraryInfo *LibInfo, + HardwareLoopInfo &HWLoopInfo); +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_XTENSA_XTENSATARGETTRANSFORMINFO_H diff --git a/llvm/test/CodeGen/Xtensa/atomicrmw.ll b/llvm/test/CodeGen/Xtensa/atomicrmw.ll new file mode 100644 index 0000000000000..f2b7526e33e84 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/atomicrmw.ll @@ -0,0 +1,103 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=xtensa -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefix=CHECK-XTENSA %s + +define i8 @atomicrmw_xchg_i8_seq_cst(i8* %a, i8 %b) nounwind { +; CHECK-XTENSA-LABEL: atomicrmw_xchg_i8_seq_cst: +; CHECK-XTENSA: # %bb.0: +; CHECK-XTENSA-NEXT: entry a1, 32 +; CHECK-XTENSA-NEXT: memw +; CHECK-XTENSA-NEXT: movi.n a8, 3 +; CHECK-XTENSA-NEXT: and a8, a8, a2 +; CHECK-XTENSA-NEXT: sub a9, a2, a8 +; CHECK-XTENSA-NEXT: slli a8, a8, 3 +; CHECK-XTENSA-NEXT: movi a10, 255 +; CHECK-XTENSA-NEXT: ssl a8 +; CHECK-XTENSA-NEXT: movi.n a11, -1 +; CHECK-XTENSA-NEXT: sll a10, a10 +; CHECK-XTENSA-NEXT: xor a11, a10, a11 +; CHECK-XTENSA-NEXT: l32i.n a12, a9, 0 +; CHECK-XTENSA-NEXT: sll a12, a3 +; CHECK-XTENSA-NEXT: l32i.n a13, a9, 0 +; CHECK-XTENSA-NEXT: and a14, a13, a10 +; CHECK-XTENSA-NEXT: .LBB0_1: # =>This Loop Header: Depth=1 +; CHECK-XTENSA-NEXT: # Child Loop BB0_2 Depth 2 +; CHECK-XTENSA-NEXT: mov.n a13, a14 +; CHECK-XTENSA-NEXT: memw +; CHECK-XTENSA-NEXT: l32i.n a14, a9, 0 +; CHECK-XTENSA-NEXT: and a7, a14, a11 +; CHECK-XTENSA-NEXT: .LBB0_2: # Parent Loop BB0_1 Depth=1 +; CHECK-XTENSA-NEXT: # => This Inner Loop Header: Depth=2 +; CHECK-XTENSA-NEXT: mov.n a15, a7 +; CHECK-XTENSA-NEXT: or a14, a12, a15 +; CHECK-XTENSA-NEXT: or a7, a13, a15 +; CHECK-XTENSA-NEXT: wsr a7, scompare1 +; CHECK-XTENSA-NEXT: s32c1i a14, a9, 0 +; CHECK-XTENSA-NEXT: beq a7, a14, .LBB0_4 +; CHECK-XTENSA-NEXT: # %bb.3: # in Loop: Header=BB0_2 Depth=2 +; CHECK-XTENSA-NEXT: and a7, a14, a11 +; CHECK-XTENSA-NEXT: bne a7, a15, .LBB0_2 +; CHECK-XTENSA-NEXT: .LBB0_4: # in Loop: Header=BB0_1 Depth=1 +; CHECK-XTENSA-NEXT: and a14, a14, a10 +; CHECK-XTENSA-NEXT: bne a14, a13, .LBB0_1 +; CHECK-XTENSA-NEXT: # %bb.5: +; CHECK-XTENSA-NEXT: ssr a8 +; CHECK-XTENSA-NEXT: srl a8, a14 +; CHECK-XTENSA-NEXT: sext a2, a8, 7 +; CHECK-XTENSA-NEXT: memw +; CHECK-XTENSA-NEXT: retw.n + + %1 = atomicrmw xchg i8* %a, i8 %b seq_cst + ret i8 %1 +} + +define i16 @atomicrmw_xchg_i16_seq_cst(i16* %a, i16 %b) nounwind { +; CHECK-XTENSA-LABEL: atomicrmw_xchg_i16_seq_cst: +; CHECK-XTENSA: # %bb.0: +; CHECK-XTENSA-NEXT: entry a1, 32 +; CHECK-XTENSA-NEXT: memw +; CHECK-XTENSA-NEXT: movi.n a8, 3 +; CHECK-XTENSA-NEXT: and a8, a8, a2 +; CHECK-XTENSA-NEXT: sub a9, a2, a8 +; CHECK-XTENSA-NEXT: slli a8, a8, 3 +; CHECK-XTENSA-NEXT: movi.n a10, 1 +; CHECK-XTENSA-NEXT: slli a10, a10, 16 +; CHECK-XTENSA-NEXT: addi.n a10, a10, -1 +; CHECK-XTENSA-NEXT: ssl a8 +; CHECK-XTENSA-NEXT: movi.n a11, -1 +; CHECK-XTENSA-NEXT: sll a10, a10 +; CHECK-XTENSA-NEXT: xor a11, a10, a11 +; CHECK-XTENSA-NEXT: l32i.n a12, a9, 0 +; CHECK-XTENSA-NEXT: sll a12, a3 +; CHECK-XTENSA-NEXT: l32i.n a13, a9, 0 +; CHECK-XTENSA-NEXT: and a14, a13, a10 +; CHECK-XTENSA-NEXT: .LBB1_1: # =>This Loop Header: Depth=1 +; CHECK-XTENSA-NEXT: # Child Loop BB1_2 Depth 2 +; CHECK-XTENSA-NEXT: mov.n a13, a14 +; CHECK-XTENSA-NEXT: memw +; CHECK-XTENSA-NEXT: l32i.n a14, a9, 0 +; CHECK-XTENSA-NEXT: and a7, a14, a11 +; CHECK-XTENSA-NEXT: .LBB1_2: # Parent Loop BB1_1 Depth=1 +; CHECK-XTENSA-NEXT: # => This Inner Loop Header: Depth=2 +; CHECK-XTENSA-NEXT: mov.n a15, a7 +; CHECK-XTENSA-NEXT: or a14, a12, a15 +; CHECK-XTENSA-NEXT: or a7, a13, a15 +; CHECK-XTENSA-NEXT: wsr a7, scompare1 +; CHECK-XTENSA-NEXT: s32c1i a14, a9, 0 +; CHECK-XTENSA-NEXT: beq a7, a14, .LBB1_4 +; CHECK-XTENSA-NEXT: # %bb.3: # in Loop: Header=BB1_2 Depth=2 +; CHECK-XTENSA-NEXT: and a7, a14, a11 +; CHECK-XTENSA-NEXT: bne a7, a15, .LBB1_2 +; CHECK-XTENSA-NEXT: .LBB1_4: # in Loop: Header=BB1_1 Depth=1 +; CHECK-XTENSA-NEXT: and a14, a14, a10 +; CHECK-XTENSA-NEXT: bne a14, a13, .LBB1_1 +; CHECK-XTENSA-NEXT: # %bb.5: +; CHECK-XTENSA-NEXT: ssr a8 +; CHECK-XTENSA-NEXT: srl a8, a14 +; CHECK-XTENSA-NEXT: sext a2, a8, 15 +; CHECK-XTENSA-NEXT: memw +; CHECK-XTENSA-NEXT: retw.n + + %1 = atomicrmw xchg i16* %a, i16 %b seq_cst + ret i16 %1 +} diff --git a/llvm/test/CodeGen/Xtensa/calling-conv-call8.ll b/llvm/test/CodeGen/Xtensa/calling-conv-call8.ll new file mode 100644 index 0000000000000..5d82100679384 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/calling-conv-call8.ll @@ -0,0 +1,172 @@ +; RUN: llc -mtriple=xtensa -mcpu=esp32 -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefix=XTENSA-STRUCT16 %s +; RUN: llc -mtriple=xtensa -mcpu=esp32 -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefix=XTENSA-I128 %s + +%struct.S = type { [4 x i32] } + +@v = dso_local global i32 0, align 4 + +define dso_local void @caller_struct_a128b_1([4 x i32] %0) { + +; XTENSA-STRUCT16-LABEL: caller_struct_a128b_1: +; XTENSA-STRUCT16-NEXT: .cfi_startproc +; XTENSA-STRUCT16: # %bb.0: +; XTENSA-STRUCT16-NEXT: entry a1, 32 +; XTENSA-STRUCT16-NEXT: .cfi_def_cfa_offset 32 +; XTENSA-STRUCT16-NEXT: l32r a8, .LCPI0_0 +; XTENSA-STRUCT16-NEXT: mov.n a10, a2 +; XTENSA-STRUCT16-NEXT: mov.n a11, a3 +; XTENSA-STRUCT16-NEXT: mov.n a12, a4 +; XTENSA-STRUCT16-NEXT: mov.n a13, a5 +; XTENSA-STRUCT16-NEXT: callx8 a8 +; XTENSA-STRUCT16-NEXT: retw.n + + call void @callee_struct_a128b_1([4 x i32] %0) + ret void +} + +declare dso_local void @callee_struct_a128b_1([4 x i32]) + +define dso_local void @caller_struct_a128b_2([4 x i32] %0) { +; XTENSA-STRUCT16-LABEL: caller_struct_a128b_2: +; XTENSA-STRUCT16: .cfi_startproc +; XTENSA-STRUCT16-NEXT: # %bb.0: +; XTENSA-STRUCT16-NEXT: entry a1, 32 +; XTENSA-STRUCT16-NEXT: .cfi_def_cfa_offset 32 +; XTENSA-STRUCT16-NEXT: l32r a8, .LCPI1_0 +; XTENSA-STRUCT16-NEXT: l32i.n a14, a8, 0 +; XTENSA-STRUCT16-NEXT: l32r a8, .LCPI1_1 +; XTENSA-STRUCT16-NEXT: mov.n a10, a2 +; XTENSA-STRUCT16-NEXT: mov.n a11, a3 +; XTENSA-STRUCT16-NEXT: mov.n a12, a4 +; XTENSA-STRUCT16-NEXT: mov.n a13, a5 +; XTENSA-STRUCT16-NEXT: callx8 a8 +; XTENSA-STRUCT16-NEXT: retw.n + + %2 = load i32, i32* @v, align 4 + call void @callee_struct_a128b_2([4 x i32] %0, i32 noundef %2) + ret void +} + +declare dso_local void @callee_struct_a128b_2([4 x i32], i32 noundef) + +define dso_local void @caller_struct_a128b_3([4 x i32] %0) { +; XTENSA-STRUCT16-LABEL: caller_struct_a128b_3: +; XTENSA-STRUCT16: .cfi_startproc +; XTENSA-STRUCT16-NEXT: # %bb.0: +; XTENSA-STRUCT16-NEXT: entry a1, 64 +; XTENSA-STRUCT16-NEXT: .cfi_def_cfa_offset 64 +; XTENSA-STRUCT16-NEXT: s32i.n a5, a1, 28 +; XTENSA-STRUCT16-NEXT: s32i.n a4, a1, 24 +; XTENSA-STRUCT16-NEXT: s32i.n a3, a1, 20 +; XTENSA-STRUCT16-NEXT: s32i.n a2, a1, 16 +; XTENSA-STRUCT16-NEXT: l32r a8, .LCPI2_0 +; XTENSA-STRUCT16-NEXT: l32i.n a10, a8, 0 +; XTENSA-STRUCT16-NEXT: l32i.n a8, a1, 28 +; XTENSA-STRUCT16-NEXT: s32i.n a8, a1, 12 +; XTENSA-STRUCT16-NEXT: l32i.n a8, a1, 24 +; XTENSA-STRUCT16-NEXT: s32i.n a8, a1, 8 +; XTENSA-STRUCT16-NEXT: l32i.n a8, a1, 20 +; XTENSA-STRUCT16-NEXT: s32i.n a8, a1, 4 +; XTENSA-STRUCT16-NEXT: l32i.n a8, a1, 16 +; XTENSA-STRUCT16-NEXT: s32i.n a8, a1, 0 +; XTENSA-STRUCT16-NEXT: l32r a8, .LCPI2_1 +; XTENSA-STRUCT16-NEXT: callx8 a8 +; XTENSA-STRUCT16-NEXT: retw.n + + %2 = alloca %struct.S, align 16 + %3 = extractvalue [4 x i32] %0, 0 + %4 = getelementptr inbounds %struct.S, %struct.S* %2, i32 0, i32 0, i32 0 + store i32 %3, i32* %4, align 16 + %5 = extractvalue [4 x i32] %0, 1 + %6 = getelementptr inbounds %struct.S, %struct.S* %2, i32 0, i32 0, i32 1 + store i32 %5, i32* %6, align 4 + %7 = extractvalue [4 x i32] %0, 2 + %8 = getelementptr inbounds %struct.S, %struct.S* %2, i32 0, i32 0, i32 2 + store i32 %7, i32* %8, align 8 + %9 = extractvalue [4 x i32] %0, 3 + %10 = getelementptr inbounds %struct.S, %struct.S* %2, i32 0, i32 0, i32 3 + store i32 %9, i32* %10, align 4 + %11 = load i32, i32* @v, align 4 + call void @callee_struct_a128b_3(i32 noundef %11, %struct.S* noundef nonnull byval(%struct.S) align 16 %2) + ret void +} + +declare dso_local void @callee_struct_a128b_3(i32 noundef, %struct.S* noundef byval(%struct.S) align 16) + +define dso_local void @caller_i128b_1(i128 noundef %0) { +; XTENSA-I128-LABEL: caller_i128b_1: +; XTENSA-I128: .cfi_startproc +; XTENSA-I128-NEXT: # %bb.0: +; XTENSA-I128-NEXT: entry a1, 32 +; XTENSA-I128-NEXT: .cfi_def_cfa_offset 32 +; XTENSA-I128-NEXT: l32r a8, .LCPI3_0 +; XTENSA-I128-NEXT: mov.n a10, a2 +; XTENSA-I128-NEXT: mov.n a11, a3 +; XTENSA-I128-NEXT: mov.n a12, a4 +; XTENSA-I128-NEXT: mov.n a13, a5 +; XTENSA-I128-NEXT: callx8 a8 +; XTENSA-I128-NEXT: retw.n + + call void @callee_i128b_1(i128 noundef %0) + ret void +} + +declare dso_local void @callee_i128b_1(i128 noundef) + +define dso_local void @caller_i128b_2(i128 noundef %0) { +; XTENSA-I128-LABEL: caller_i128b_2: +; XTENSA-I128: .cfi_startproc +; XTENSA-I128-NEXT: # %bb.0: +; XTENSA-I128-NEXT: entry a1, 32 +; XTENSA-I128-NEXT: .cfi_def_cfa_offset 32 +; XTENSA-I128-NEXT: l32r a8, .LCPI4_0 +; XTENSA-I128-NEXT: l32i.n a14, a8, 0 +; XTENSA-I128-NEXT: l32r a8, .LCPI4_1 +; XTENSA-I128-NEXT: mov.n a10, a2 +; XTENSA-I128-NEXT: mov.n a11, a3 +; XTENSA-I128-NEXT: mov.n a12, a4 +; XTENSA-I128-NEXT: mov.n a13, a5 +; XTENSA-I128-NEXT: callx8 a8 +; XTENSA-I128-NEXT: retw.n + + %2 = load i32, i32* @v, align 4 + call void @callee_i128b_2(i128 noundef %0, i32 noundef %2) + ret void +} + +declare dso_local void @callee_i128b_2(i128 noundef, i32 noundef) + +define dso_local void @caller_i128b_3(i128 noundef %0) { +; XTENSA-I128-LABEL: caller_i128b_3: +; XTENSA-I128: .cfi_startproc +; XTENSA-I128-NEXT: # %bb.0: +; XTENSA-I128-NEXT: entry a1, 64 +; XTENSA-I128-NEXT: .cfi_def_cfa_offset 64 +; XTENSA-I128-NEXT: s32i.n a5, a1, 28 +; XTENSA-I128-NEXT: s32i.n a4, a1, 24 +; XTENSA-I128-NEXT: s32i.n a3, a1, 20 +; XTENSA-I128-NEXT: s32i.n a2, a1, 16 +; XTENSA-I128-NEXT: l32r a8, .LCPI5_0 +; XTENSA-I128-NEXT: l32i.n a10, a8, 0 +; XTENSA-I128-NEXT: l32i.n a8, a1, 28 +; XTENSA-I128-NEXT: s32i.n a8, a1, 12 +; XTENSA-I128-NEXT: l32i.n a8, a1, 24 +; XTENSA-I128-NEXT: s32i.n a8, a1, 8 +; XTENSA-I128-NEXT: l32i.n a8, a1, 20 +; XTENSA-I128-NEXT: s32i.n a8, a1, 4 +; XTENSA-I128-NEXT: l32i.n a8, a1, 16 +; XTENSA-I128-NEXT: s32i.n a8, a1, 0 +; XTENSA-I128-NEXT: l32r a8, .LCPI5_1 +; XTENSA-I128-NEXT: callx8 a8 +; XTENSA-I128-NEXT: retw.n + + %2 = alloca i128, align 16 + %3 = load i32, i32* @v, align 4 + store i128 %0, i128* %2, align 16 + call void @callee_i128b_3(i32 noundef %3, i128* noundef nonnull byval(i128) align 16 %2) + ret void +} + +declare dso_local void @callee_i128b_3(i32 noundef, i128* noundef byval(i128) align 16) diff --git a/llvm/test/CodeGen/Xtensa/float-intrinsics.ll b/llvm/test/CodeGen/Xtensa/float-intrinsics.ll new file mode 100644 index 0000000000000..256a1dee2abf8 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/float-intrinsics.ll @@ -0,0 +1,363 @@ +; RUN: llc -mtriple=xtensa -mcpu=esp32 -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefix=XTENSA %s + +declare float @llvm.sqrt.f32(float) + +define float @sqrt_f32(float %a) nounwind { +; XTENSA: .literal .LCPI0_0, sqrtf +; XTENSA-LABEL: sqrt_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI0_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n +; + %1 = call float @llvm.sqrt.f32(float %a) + ret float %1 +} + +declare float @llvm.powi.f32(float, i32) + +define float @powi_f32(float %a, i32 %b) nounwind { +; XTENSA: .literal .LCPI1_0, __powisf2 +; XTENSA-LABEL: powi_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI1_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: mov.n a11, a3 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.powi.f32(float %a, i32 %b) + ret float %1 +} + +declare float @llvm.sin.f32(float) + +define float @sin_f32(float %a) nounwind { +; XTENSA: .literal .LCPI2_0, sinf +; XTENSA-LABEL: sin_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI2_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.sin.f32(float %a) + ret float %1 +} + +declare float @llvm.cos.f32(float) + +define float @cos_f32(float %a) nounwind { +; XTENSA: .literal .LCPI3_0, cosf +; XTENSA-LABEL: cos_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI3_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.cos.f32(float %a) + ret float %1 +} + +declare float @llvm.pow.f32(float, float) + +define float @pow_f32(float %a, float %b) nounwind { +; XTENSA: .literal .LCPI4_0, powf +; XTENSA-LABEL: pow_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI4_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: mov.n a11, a3 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + %1 = call float @llvm.pow.f32(float %a, float %b) + ret float %1 +} + +declare float @llvm.exp.f32(float) + +define float @exp_f32(float %a) nounwind { +; XTENSA: .literal .LCPI5_0, expf +; XTENSA-LABEL: exp_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI5_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + %1 = call float @llvm.exp.f32(float %a) + ret float %1 +} + +declare float @llvm.exp2.f32(float) + +define float @exp2_f32(float %a) nounwind { +; XTENSA: .literal .LCPI6_0, exp2 +; XTENSA-LABEL: exp2_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI6_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + %1 = call float @llvm.exp2.f32(float %a) + ret float %1 +} + +declare float @llvm.log.f32(float) + +define float @log_f32(float %a) nounwind { +; XTENSA: .literal .LCPI7_0, log +; XTENSA-LABEL: log_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI7_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + %1 = call float @llvm.log.f32(float %a) + ret float %1 +} + +declare float @llvm.log10.f32(float) + +define float @log10_f32(float %a) nounwind { +; XTENSA: .literal .LCPI8_0, log10 +; XTENSA-LABEL: log10_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI8_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + %1 = call float @llvm.log10.f32(float %a) + ret float %1 +} + +declare float @llvm.log2.f32(float) + +define float @log2_f32(float %a) nounwind { +; XTENSA: .literal .LCPI9_0, log2 +; XTENSA-LABEL: log2_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI9_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + %1 = call float @llvm.log2.f32(float %a) + ret float %1 +} + +declare float @llvm.fma.f32(float, float, float) + +define float @fma_f32(float %a, float %b, float %c) nounwind { +; XTENSA-LABEL: fma_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: wfr f8, a3 +; XTENSA-NEXT: wfr f9, a2 +; XTENSA-NEXT: wfr f10, a4 +; XTENSA-NEXT: madd.s f10, f9, f8 +; XTENSA-NEXT: rfr a2, f10 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.fma.f32(float %a, float %b, float %c) + ret float %1 +} + +declare float @llvm.minnum.f32(float, float) + +define float @minnum_f32(float %a, float %b) nounwind { +; XTENSA: .literal .LCPI11_0, fminf +; XTENSA-LABEL: minnum_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI11_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: mov.n a11, a3 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.minnum.f32(float %a, float %b) + ret float %1 +} + +declare float @llvm.maxnum.f32(float, float) + +define float @maxnum_f32(float %a, float %b) nounwind { +; XTENSA: .literal .LCPI12_0, fmaxf +; XTENSA-LABEL: maxnum_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI12_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: mov.n a11, a3 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.maxnum.f32(float %a, float %b) + ret float %1 +} + +declare float @llvm.fabs.f32(float) + +define float @fabs_f32(float %a) nounwind { +; XTENSA: .literal .LCPI13_0, 2147483647 +; XTENSA-LABEL: fabs_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI13_0 +; XTENSA-NEXT: and a2, a2, a8 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.fabs.f32(float %a) + ret float %1 +} + +declare float @llvm.copysign.f32(float, float) + +define float @copysign_f32(float %a, float %b) nounwind { +; XTENSA: .literal .LCPI14_0, -2147483648 +; XTENSA: .literal .LCPI14_1, 2147483647 +; XTENSA-LABEL: copysign_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI14_0 +; XTENSA-NEXT: and a8, a3, a8 +; XTENSA-NEXT: l32r a9, .LCPI14_1 +; XTENSA-NEXT: and a9, a2, a9 +; XTENSA-NEXT: wfr f8, a9 +; XTENSA-NEXT: movi.n a9, 0 +; XTENSA-NEXT: beq a8, a9, .LBB14_2 +; XTENSA-NEXT: # %bb.1: +; XTENSA-NEXT: neg.s f8, f8 +; XTENSA-NEXT: .LBB14_2: +; XTENSA-NEXT: rfr a2, f8 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.copysign.f32(float %a, float %b) + ret float %1 +} + +declare float @llvm.floor.f32(float) + +define float @floor_f32(float %a) nounwind { +; XTENSA: .literal .LCPI15_0, floor +; XTENSA-LABEL: floor_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI15_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.floor.f32(float %a) + ret float %1 +} + +declare float @llvm.ceil.f32(float) + +define float @ceil_f32(float %a) nounwind { +; XTENSA: .literal .LCPI16_0, ceil +; XTENSA-LABEL: ceil_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI16_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.ceil.f32(float %a) + ret float %1 +} + +declare float @llvm.trunc.f32(float) + +define float @trunc_f32(float %a) nounwind { +; XTENSA: .literal .LCPI17_0, trunc +; XTENSA-LABEL: trunc_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI17_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.trunc.f32(float %a) + ret float %1 +} + +declare float @llvm.rint.f32(float) + +define float @rint_f32(float %a) nounwind { +; XTENSA: .literal .LCPI18_0, rint +; XTENSA-LABEL: rint_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI18_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.rint.f32(float %a) + ret float %1 +} + +declare float @llvm.nearbyint.f32(float) + +define float @nearbyint_f32(float %a) nounwind { +; XTENSA: .literal .LCPI19_0, nearbyint +; XTENSA-LABEL: nearbyint_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI19_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.nearbyint.f32(float %a) + ret float %1 +} + +declare float @llvm.round.f32(float) + +define float @round_f32(float %a) nounwind { +; XTENSA: .literal .LCPI20_0, round +; XTENSA-LABEL: round_f32: +; XTENSA: # %bb.0: +; XTENSA-NEXT: entry a1, 32 +; XTENSA-NEXT: l32r a8, .LCPI20_0 +; XTENSA-NEXT: mov.n a10, a2 +; XTENSA-NEXT: callx8 a8 +; XTENSA-NEXT: mov.n a2, a10 +; XTENSA-NEXT: retw.n + + %1 = call float @llvm.round.f32(float %a) + ret float %1 +} diff --git a/llvm/test/CodeGen/Xtensa/funnel-shift.ll b/llvm/test/CodeGen/Xtensa/funnel-shift.ll new file mode 100644 index 0000000000000..61eed40e72b9f --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/funnel-shift.ll @@ -0,0 +1,22 @@ +; RUN: llc -O1 -mtriple=xtensa -mcpu=esp32 %s -o - | FileCheck %s + +define dso_local i32 @test_fshr(i32 %value1, i32 %value2, i32 %shift) nounwind { +; CHECK-LABEL: @test_fshr +; CHECK: ssr a4 +; CHECK: src a2, a2, a3 +entry: + %0 = tail call i32 @llvm.fshr.i32(i32 %value1, i32 %value2, i32 %shift) + ret i32 %0 +} + +define dso_local i32 @test_fshl(i32 %value1, i32 %value2, i32 %shift) nounwind { +; CHECK-LABEL: @test_fshl +; CHECK: ssl a4 +; CHECK: src a2, a2, a3 +entry: + %0 = tail call i32 @llvm.fshl.i32(i32 %value1, i32 %value2, i32 %shift) + ret i32 %0 +} + +declare i32 @llvm.fshr.i32(i32, i32, i32) nounwind +declare i32 @llvm.fshl.i32(i32, i32, i32) nounwind diff --git a/llvm/test/CodeGen/Xtensa/hwloop_inner_loop.ll b/llvm/test/CodeGen/Xtensa/hwloop_inner_loop.ll new file mode 100644 index 0000000000000..ba188d61b5c8e --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/hwloop_inner_loop.ll @@ -0,0 +1,31 @@ +; RUN: llc -O1 -mtriple=xtensa -mcpu=esp32 -disable-xtensa-hwloops=false %s -o - | FileCheck %s + + +; Function Attrs: norecurse nounwind optsize readnone +define i32 @test_hwloop(i32 %a, i32 %b, i32 %n) local_unnamed_addr #0 { +; CHECK-LABEL: @test_hwloop +entry: + %cmp7 = icmp sgt i32 %n, 0 + br i1 %cmp7, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.body, %entry + %a.addr.0.lcssa = phi i32 [ %a, %entry ], [ %a.addr.1, %for.body ] + ret i32 %a.addr.0.lcssa + +for.body: ; preds = %entry, %for.body + %i.09 = phi i32 [ %inc, %for.body ], [ 0, %entry ] + %a.addr.08 = phi i32 [ %a.addr.1, %for.body ], [ %a, %entry ] + %cmp1 = icmp sgt i32 %a.addr.08, 0 + %mul = mul nsw i32 %a.addr.08, %b + %add = select i1 %cmp1, i32 %mul, i32 0 + %a.addr.1 = add nsw i32 %add, %a.addr.08 + %inc = add nuw nsw i32 %i.09, 1 + %cmp = icmp slt i32 %inc, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup +; CHECK: loop a4, .LBB0_5 +; CHECK: bge a8, a2, .LBB0_2 +; CHECK: mull a9, a2, a3 +; CHECK: add.n a2, a9, a2 +; CHECK: .LBB0_5 +} + diff --git a/llvm/test/CodeGen/Xtensa/hwloop_unsuitable_loop.ll b/llvm/test/CodeGen/Xtensa/hwloop_unsuitable_loop.ll new file mode 100644 index 0000000000000..c8cf121eb4107 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/hwloop_unsuitable_loop.ll @@ -0,0 +1,38 @@ +; RUN: llc -O1 -mtriple=xtensa -mcpu=esp32 -disable-xtensa-hwloops=false %s -o - | FileCheck %s + +; Function Attrs: nounwind optsize +define i32 @test_hwloop(i32 %a, i32 %b, i32 %n) local_unnamed_addr #1 { +; CHECK-LABEL: @test_hwloop +entry: + %cmp7 = icmp sgt i32 %n, 0 + br i1 %cmp7, label %for.body, label %for.cond.cleanup + +for.cond.cleanup: ; preds = %for.body, %entry + %a.addr.0.lcssa = phi i32 [ %a, %entry ], [ %a.addr.1, %for.body ] + ret i32 %a.addr.0.lcssa + +for.body: ; preds = %entry, %for.body + %i.09 = phi i32 [ %inc, %for.body ], [ 0, %entry ] + %a.addr.08 = phi i32 [ %a.addr.1, %for.body ], [ %a, %entry ] + tail call void asm sideeffect "", ""() #2, !srcloc !2 + %cmp1 = icmp sgt i32 %a.addr.08, 0 + %mul = mul nsw i32 %a.addr.08, %b + %add = select i1 %cmp1, i32 %mul, i32 0 + %a.addr.1 = add nsw i32 %add, %a.addr.08 + %inc = add nuw nsw i32 %i.09, 1 + %cmp = icmp slt i32 %inc, %n + br i1 %cmp, label %for.body, label %for.cond.cleanup +; CHECK:.LBB0_2: +; CHECK: mov.n a9, a8 +; CHECK:.LBB0_3: +; CHECK: add.n a2, a9, a2 +; CHECK: #APP +; CHECK: #NO_APP +; CHECK: addi.n a4, a4, -1 +; CHECK: beqz a4, .LBB0_6 +; CHECK:.LBB0_4: +; CHECK: bge a8, a2, .LBB0_2 + +} + +!2 = !{i32 216} diff --git a/llvm/test/CodeGen/Xtensa/inline-asm-constraints.ll b/llvm/test/CodeGen/Xtensa/inline-asm-constraints.ll new file mode 100644 index 0000000000000..7dbb0f07debda --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/inline-asm-constraints.ll @@ -0,0 +1,23 @@ +; RUN: llc -mtriple=xtensa -mcpu=esp32 -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefix=Xtensa %s + + +@gf = external global float + +define float @constraint_f_float(float %a) nounwind { +; Xtensa-LABEL: constraint_f_float: +; Xtensa: # %bb.0: +; Xtensa-NEXT: entry a1, 32 +; Xtensa-NEXT: wfr f8, a2 +; Xtensa-NEXT: l32r a8, .LCPI0_0 +; Xtensa-NEXT: lsi f9, a8, 0 +; Xtensa-NEXT: #APP +; Xtensa-NEXT: add.s f8, f8, f9 +; Xtensa-NEXT: #NO_APP +; Xtensa-NEXT: rfr a2, f8 +; Xtensa-NEXT: retw + %1 = load float, float* @gf + %2 = tail call float asm "add.s $0, $1, $2", "=f,f,f"(float %a, float %1) + ret float %2 +} + diff --git a/llvm/test/CodeGen/Xtensa/inline-asm-invalid.ll b/llvm/test/CodeGen/Xtensa/inline-asm-invalid.ll new file mode 100644 index 0000000000000..260429d933446 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/inline-asm-invalid.ll @@ -0,0 +1,8 @@ +; RUN: not llc -mtriple=xtensa -mcpu=generic < %s 2>&1 | FileCheck %s + +define void @constraint_f() nounwind { +; CHECK: error: couldn't allocate input reg for constraint 'f' + tail call void asm "add.s f0, f1, $0", "f"(float 0.0) + ret void +} + diff --git a/llvm/test/CodeGen/Xtensa/inline-asm.ll b/llvm/test/CodeGen/Xtensa/inline-asm.ll new file mode 100644 index 0000000000000..7a267d5145981 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/inline-asm.ll @@ -0,0 +1,20 @@ +; RUN: llc -mtriple=xtensa -mcpu=esp32 -verify-machineinstrs < %s \ +; RUN: | FileCheck -check-prefix=Xtensa %s + + +define dso_local i32 @test_memptr(i32 noundef %0) local_unnamed_addr #0 { +; Xtensa-LABEL: test_memptr: +; Xtensa: # %bb.0: +; Xtensa-NEXT: entry a1, 32 +; Xtensa-NEXT: #APP +; Xtensa-NEXT: l32i a2, a2, 0 +; Xtensa-NEXT: #NO_APP +; Xtensa-NEXT: retw + %2 = inttoptr i32 %0 to i32* + %3 = call i32 asm sideeffect "l32i $0, $1", "=r,*m"(i32* elementtype(i32) %2) + ret i32 %3 +} + +attributes #0 = { nounwind } +attributes #1 = { nounwind } + diff --git a/llvm/test/CodeGen/Xtensa/lit.local.cfg b/llvm/test/CodeGen/Xtensa/lit.local.cfg new file mode 100644 index 0000000000000..9e0be9979305d --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'Xtensa' in config.root.targets: + config.unsupported = True diff --git a/llvm/test/CodeGen/Xtensa/mac16_intrinsics.ll b/llvm/test/CodeGen/Xtensa/mac16_intrinsics.ll new file mode 100644 index 0000000000000..fd58c76872aff --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/mac16_intrinsics.ll @@ -0,0 +1,319 @@ +; RUN: llc -O1 -mtriple=xtensa -mcpu=esp32 %s -o - | FileCheck %s + +define void @test_xtensa_umul(i32 %a, i32 %b) nounwind { +; CHECK-LABEL: test_xtensa_umul +; CHECK: umul.aa.ll a2, a3 + call void @llvm.xtensa.umul.aa.ll(i32 %a, i32 %b) +; CHECK: umul.aa.lh a2, a3 + call void @llvm.xtensa.umul.aa.lh(i32 %a, i32 %b) +; CHECK: umul.aa.hl a2, a3 + call void @llvm.xtensa.umul.aa.hl(i32 %a, i32 %b) +; CHECK: umul.aa.hh a2, a3 + call void @llvm.xtensa.umul.aa.hh(i32 %a, i32 %b) + ret void +} + +define void @test_xtensa_mul(i32 %a, i32 %b) nounwind { +; CHECK-LABEL: test_xtensa_mul +; CHECK: mul.aa.ll a2, a3 + call void @llvm.xtensa.mul.aa.ll(i32 %a, i32 %b) +; CHECK: mul.aa.lh a2, a3 + call void @llvm.xtensa.mul.aa.lh(i32 %a, i32 %b) +; CHECK: mul.aa.hl a2, a3 + call void @llvm.xtensa.mul.aa.hl(i32 %a, i32 %b) +; CHECK: mul.aa.hh a2, a3 + call void @llvm.xtensa.mul.aa.hh(i32 %a, i32 %b) +; CHECK: mul.ad.ll a2, m2 + call void @llvm.xtensa.mul.ad.ll(i32 %a, i32 2) +; CHECK: mul.ad.lh a2, m2 + call void @llvm.xtensa.mul.ad.lh(i32 %a, i32 2) +; CHECK: mul.ad.hl a2, m2 + call void @llvm.xtensa.mul.ad.hl(i32 %a, i32 2) +; CHECK: mul.ad.hh a2, m2 + call void @llvm.xtensa.mul.ad.hh(i32 %a, i32 2) +; CHECK: mul.da.ll m1, a3 + call void @llvm.xtensa.mul.da.ll(i32 1, i32 %b) +; CHECK: mul.da.lh m1, a3 + call void @llvm.xtensa.mul.da.lh(i32 1, i32 %b) +; CHECK: mul.da.hl m1, a3 + call void @llvm.xtensa.mul.da.hl(i32 1, i32 %b) +; CHECK: mul.da.hh m1, a3 + call void @llvm.xtensa.mul.da.hh(i32 1, i32 %b) +; CHECK: mul.dd.ll m1, m2 + call void @llvm.xtensa.mul.dd.ll(i32 1, i32 2) +; CHECK: mul.dd.lh m1, m2 + call void @llvm.xtensa.mul.dd.lh(i32 1, i32 2) +; CHECK: mul.dd.hl m1, m2 + call void @llvm.xtensa.mul.dd.hl(i32 1, i32 2) +; CHECK: mul.dd.hh m1, m2 + call void @llvm.xtensa.mul.dd.hh(i32 1, i32 2) + ret void +} + +define void @test_xtensa_mula(i32 %a, i32 %b) nounwind { +; CHECK-LABEL: test_xtensa_mula +; CHECK: mula.aa.ll a2, a3 + call void @llvm.xtensa.mula.aa.ll(i32 %a, i32 %b) +; CHECK: mula.aa.lh a2, a3 + call void @llvm.xtensa.mula.aa.lh(i32 %a, i32 %b) +; CHECK: mula.aa.hl a2, a3 + call void @llvm.xtensa.mula.aa.hl(i32 %a, i32 %b) +; CHECK: mula.aa.hh a2, a3 + call void @llvm.xtensa.mula.aa.hh(i32 %a, i32 %b) +; CHECK: mula.ad.ll a2, m2 + call void @llvm.xtensa.mula.ad.ll(i32 %a, i32 2) +; CHECK: mula.ad.lh a2, m2 + call void @llvm.xtensa.mula.ad.lh(i32 %a, i32 2) +; CHECK: mula.ad.hl a2, m2 + call void @llvm.xtensa.mula.ad.hl(i32 %a, i32 2) +; CHECK: mula.ad.hh a2, m2 + call void @llvm.xtensa.mula.ad.hh(i32 %a, i32 2) +; CHECK: mula.da.ll m1, a3 + call void @llvm.xtensa.mula.da.ll(i32 1, i32 %b) +; CHECK: mula.da.lh m1, a3 + call void @llvm.xtensa.mula.da.lh(i32 1, i32 %b) +; CHECK: mula.da.hl m1, a3 + call void @llvm.xtensa.mula.da.hl(i32 1, i32 %b) +; CHECK: mula.da.hh m1, a3 + call void @llvm.xtensa.mula.da.hh(i32 1, i32 %b) +; CHECK: mula.dd.ll m1, m2 + call void @llvm.xtensa.mula.dd.ll(i32 1, i32 2) +; CHECK: mula.dd.lh m1, m2 + call void @llvm.xtensa.mula.dd.lh(i32 1, i32 2) +; CHECK: mula.dd.hl m1, m2 + call void @llvm.xtensa.mula.dd.hl(i32 1, i32 2) +; CHECK: mula.dd.hh m1, m2 + call void @llvm.xtensa.mula.dd.hh(i32 1, i32 2) + ret void +} + +define void @test_xtensa_muls(i32 %a, i32 %b) nounwind { +; CHECK-LABEL: test_xtensa_muls +; CHECK: muls.aa.ll a2, a3 + call void @llvm.xtensa.muls.aa.ll(i32 %a, i32 %b) +; CHECK: muls.aa.lh a2, a3 + call void @llvm.xtensa.muls.aa.lh(i32 %a, i32 %b) +; CHECK: muls.aa.hl a2, a3 + call void @llvm.xtensa.muls.aa.hl(i32 %a, i32 %b) +; CHECK: muls.aa.hh a2, a3 + call void @llvm.xtensa.muls.aa.hh(i32 %a, i32 %b) +; CHECK: muls.ad.ll a2, m2 + call void @llvm.xtensa.muls.ad.ll(i32 %a, i32 2) +; CHECK: muls.ad.lh a2, m2 + call void @llvm.xtensa.muls.ad.lh(i32 %a, i32 2) +; CHECK: muls.ad.hl a2, m2 + call void @llvm.xtensa.muls.ad.hl(i32 %a, i32 2) +; CHECK: muls.ad.hh a2, m2 + call void @llvm.xtensa.muls.ad.hh(i32 %a, i32 2) +; CHECK: muls.da.ll m1, a3 + call void @llvm.xtensa.muls.da.ll(i32 1, i32 %b) +; CHECK: muls.da.lh m1, a3 + call void @llvm.xtensa.muls.da.lh(i32 1, i32 %b) +; CHECK: muls.da.hl m1, a3 + call void @llvm.xtensa.muls.da.hl(i32 1, i32 %b) +; CHECK: muls.da.hh m1, a3 + call void @llvm.xtensa.muls.da.hh(i32 1, i32 %b) +; CHECK: muls.dd.ll m1, m2 + call void @llvm.xtensa.muls.dd.ll(i32 1, i32 2) +; CHECK: muls.dd.lh m1, m2 + call void @llvm.xtensa.muls.dd.lh(i32 1, i32 2) +; CHECK: muls.dd.hl m1, m2 + call void @llvm.xtensa.muls.dd.hl(i32 1, i32 2) +; CHECK: muls.dd.hh m1, m2 + call void @llvm.xtensa.muls.dd.hh(i32 1, i32 2) + ret void +} + +define void @test_xtensa_mula_ld(i32 %pa.coerce, i32 %b) nounwind { +; CHECK-LABEL: test_xtensa_mula_ld +entry: + %0 = inttoptr i32 %pa.coerce to i8* +; CHECK: mula.da.ll.lddec m1, a{{[0-9]+}}, m0, a3 + call void @llvm.xtensa.mula.da.ll.lddec(i32 1, i8* %0, i32 0, i32 %b) +; CHECK: mula.da.lh.lddec m1, a{{[0-9]+}}, m0, a3 + call void @llvm.xtensa.mula.da.lh.lddec(i32 1, i8* %0, i32 0, i32 %b) +; CHECK: mula.da.hl.lddec m1, a{{[0-9]+}}, m0, a3 + call void @llvm.xtensa.mula.da.hl.lddec(i32 1, i8* %0, i32 0, i32 %b) +; CHECK: mula.da.hh.lddec m1, a{{[0-9]+}}, m0, a3 + call void @llvm.xtensa.mula.da.hh.lddec(i32 1, i8* %0, i32 0, i32 %b) +; CHECK: mula.dd.ll.lddec m1, a{{[0-9]+}}, m0, m2 + call void @llvm.xtensa.mula.dd.ll.lddec(i32 1, i8* %0, i32 0, i32 2) +; CHECK: mula.dd.lh.lddec m1, a{{[0-9]+}}, m0, m2 + call void @llvm.xtensa.mula.dd.lh.lddec(i32 1, i8* %0, i32 0, i32 2) +; CHECK: mula.dd.hl.lddec m1, a{{[0-9]+}}, m0, m2 + call void @llvm.xtensa.mula.dd.hl.lddec(i32 1, i8* %0, i32 0, i32 2) +; CHECK: mula.dd.hh.lddec m1, a{{[0-9]+}}, m0, m2 + call void @llvm.xtensa.mula.dd.hh.lddec(i32 1, i8* %0, i32 0, i32 2) +; CHECK: mula.da.ll.ldinc m1, a{{[0-9]+}}, m0, a3 + call void @llvm.xtensa.mula.da.ll.ldinc(i32 1, i8* %0, i32 0, i32 %b) +; CHECK: mula.da.lh.ldinc m1, a{{[0-9]+}}, m0, a3 + call void @llvm.xtensa.mula.da.lh.ldinc(i32 1, i8* %0, i32 0, i32 %b) +; CHECK: mula.da.hl.ldinc m1, a{{[0-9]+}}, m0, a3 + call void @llvm.xtensa.mula.da.hl.ldinc(i32 1, i8* %0, i32 0, i32 %b) +; CHECK: mula.da.hh.ldinc m1, a{{[0-9]+}}, m0, a3 + call void @llvm.xtensa.mula.da.hh.ldinc(i32 1, i8* %0, i32 0, i32 %b) +; CHECK: mula.dd.ll.ldinc m1, a{{[0-9]+}}, m0, m2 + call void @llvm.xtensa.mula.dd.ll.ldinc(i32 1, i8* %0, i32 0, i32 2) +; CHECK: mula.dd.lh.ldinc m1, a{{[0-9]+}}, m0, m2 + call void @llvm.xtensa.mula.dd.lh.ldinc(i32 1, i8* %0, i32 0, i32 2) +; CHECK: mula.dd.hl.ldinc m1, a{{[0-9]+}}, m0, m2 + call void @llvm.xtensa.mula.dd.hl.ldinc(i32 1, i8* %0, i32 0, i32 2) +; CHECK: mula.dd.hh.ldinc m1, a{{[0-9]+}}, m0, m2 + call void @llvm.xtensa.mula.dd.hh.ldinc(i32 1, i8* %0, i32 0, i32 2) + ret void +} + +define void @test_xtensa_ld(i32 %pa.coerce) nounwind { +; CHECK-LABEL: test_xtensa_ld +entry: + %0 = inttoptr i32 %pa.coerce to i8* +; CHECK: lddec m0, a{{[0-9]+}} + call void @llvm.xtensa.lddec(i32 0, i8* %0) +; CHECK: ldinc m0, a{{[0-9]+}} + call void @llvm.xtensa.ldinc(i32 0, i8* %0) + ret void +} + +define void @test_xtensa_wsr(i32 %a) { +; CHECK-LABEL: test_xtensa_wsr +; CHECK: wsr a2, acclo + call void @llvm.xtensa.wsr.acclo(i32 %a) +; CHECK: wsr a2, acchi + call void @llvm.xtensa.wsr.acchi(i32 %a) +; CHECK: wsr a2, m0 + call void @llvm.xtensa.wsr.m0(i32 %a) +; CHECK: wsr a2, m1 + call void @llvm.xtensa.wsr.m1(i32 %a) +; CHECK: wsr a2, m2 + call void @llvm.xtensa.wsr.m2(i32 %a) +; CHECK: wsr a2, m3 + call void @llvm.xtensa.wsr.m3(i32 %a) + ret void +} + +define void @test_xtensa_xsr(i32 %a.coerce) { +; CHECK-LABEL: test_xtensa_xsr +entry: + %0 = inttoptr i32 %a.coerce to i8* +; CHECK: xsr a{{[0-9]+}}, acclo + call void @llvm.xtensa.xsr.acclo(i8* %0) +; CHECK: xsr a{{[0-9]+}}, acchi + call void @llvm.xtensa.xsr.acchi(i8* %0) +; CHECK: xsr a{{[0-9]+}}, m0 + call void @llvm.xtensa.xsr.m0(i8* %0) +; CHECK: xsr a{{[0-9]+}}, m1 + call void @llvm.xtensa.xsr.m1(i8* %0) +; CHECK: xsr a{{[0-9]+}}, m2 + call void @llvm.xtensa.xsr.m2(i8* %0) +; CHECK: xsr a{{[0-9]+}}, m3 + call void @llvm.xtensa.xsr.m3(i8* %0) + ret void +} + +define void @test_xtensa_rsr() { +; CHECK-LABEL: test_xtensa_rsr +entry: +; CHECK: rsr a{{[0-9]+}}, acclo + %0 = call i32 @llvm.xtensa.rsr.acclo() +; CHECK: rsr a{{[0-9]+}}, acchi + %1 = call i32 @llvm.xtensa.rsr.acchi() +; CHECK: rsr a{{[0-9]+}}, m0 + %2 = call i32 @llvm.xtensa.rsr.m0() +; CHECK: rsr a{{[0-9]+}}, m1 + %3 = call i32 @llvm.xtensa.rsr.m1() +; CHECK: rsr a{{[0-9]+}}, m2 + %4 = call i32 @llvm.xtensa.rsr.m2() +; CHECK: rsr a{{[0-9]+}}, m3 + %5 = call i32 @llvm.xtensa.rsr.m3() + ret void +} + +declare void @llvm.xtensa.umul.aa.ll(i32, i32) nounwind +declare void @llvm.xtensa.umul.aa.lh(i32, i32) nounwind +declare void @llvm.xtensa.umul.aa.hl(i32, i32) nounwind +declare void @llvm.xtensa.umul.aa.hh(i32, i32) nounwind +declare void @llvm.xtensa.mul.aa.ll(i32, i32) nounwind +declare void @llvm.xtensa.mul.aa.lh(i32, i32) nounwind +declare void @llvm.xtensa.mul.aa.hl(i32, i32) nounwind +declare void @llvm.xtensa.mul.aa.hh(i32, i32) nounwind +declare void @llvm.xtensa.mul.ad.ll(i32, i32 immarg) nounwind +declare void @llvm.xtensa.mul.ad.lh(i32, i32 immarg) nounwind +declare void @llvm.xtensa.mul.ad.hl(i32, i32 immarg) nounwind +declare void @llvm.xtensa.mul.ad.hh(i32, i32 immarg) nounwind +declare void @llvm.xtensa.mul.da.ll(i32 immarg, i32) nounwind +declare void @llvm.xtensa.mul.da.lh(i32 immarg, i32) nounwind +declare void @llvm.xtensa.mul.da.hl(i32 immarg, i32) nounwind +declare void @llvm.xtensa.mul.da.hh(i32 immarg, i32) nounwind +declare void @llvm.xtensa.mul.dd.ll(i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mul.dd.lh(i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mul.dd.hl(i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mul.dd.hh(i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mula.aa.ll(i32, i32) nounwind +declare void @llvm.xtensa.mula.aa.lh(i32, i32) nounwind +declare void @llvm.xtensa.mula.aa.hl(i32, i32) nounwind +declare void @llvm.xtensa.mula.aa.hh(i32, i32) nounwind +declare void @llvm.xtensa.mula.ad.ll(i32, i32 immarg) nounwind +declare void @llvm.xtensa.mula.ad.lh(i32, i32 immarg) nounwind +declare void @llvm.xtensa.mula.ad.hl(i32, i32 immarg) nounwind +declare void @llvm.xtensa.mula.ad.hh(i32, i32 immarg) nounwind +declare void @llvm.xtensa.mula.da.ll(i32 immarg, i32) nounwind +declare void @llvm.xtensa.mula.da.lh(i32 immarg, i32) nounwind +declare void @llvm.xtensa.mula.da.hl(i32 immarg, i32) nounwind +declare void @llvm.xtensa.mula.da.hh(i32 immarg, i32) nounwind +declare void @llvm.xtensa.mula.dd.ll(i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mula.dd.lh(i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mula.dd.hl(i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mula.dd.hh(i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.muls.aa.ll(i32, i32) nounwind +declare void @llvm.xtensa.muls.aa.lh(i32, i32) nounwind +declare void @llvm.xtensa.muls.aa.hl(i32, i32) nounwind +declare void @llvm.xtensa.muls.aa.hh(i32, i32) nounwind +declare void @llvm.xtensa.muls.ad.ll(i32, i32 immarg) nounwind +declare void @llvm.xtensa.muls.ad.lh(i32, i32 immarg) nounwind +declare void @llvm.xtensa.muls.ad.hl(i32, i32 immarg) nounwind +declare void @llvm.xtensa.muls.ad.hh(i32, i32 immarg) nounwind +declare void @llvm.xtensa.muls.da.ll(i32 immarg, i32) nounwind +declare void @llvm.xtensa.muls.da.lh(i32 immarg, i32) nounwind +declare void @llvm.xtensa.muls.da.hl(i32 immarg, i32) nounwind +declare void @llvm.xtensa.muls.da.hh(i32 immarg, i32) nounwind +declare void @llvm.xtensa.muls.dd.ll(i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.muls.dd.lh(i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.muls.dd.hl(i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.muls.dd.hh(i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mula.da.ll.lddec(i32 immarg, i8*, i32 immarg, i32) nounwind +declare void @llvm.xtensa.mula.da.lh.lddec(i32 immarg, i8*, i32 immarg, i32) nounwind +declare void @llvm.xtensa.mula.da.hl.lddec(i32 immarg, i8*, i32 immarg, i32) nounwind +declare void @llvm.xtensa.mula.da.hh.lddec(i32 immarg, i8*, i32 immarg, i32) nounwind +declare void @llvm.xtensa.mula.dd.ll.lddec(i32 immarg, i8*, i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mula.dd.lh.lddec(i32 immarg, i8*, i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mula.dd.hl.lddec(i32 immarg, i8*, i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mula.dd.hh.lddec(i32 immarg, i8*, i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mula.da.ll.ldinc(i32 immarg, i8*, i32 immarg, i32) nounwind +declare void @llvm.xtensa.mula.da.lh.ldinc(i32 immarg, i8*, i32 immarg, i32) nounwind +declare void @llvm.xtensa.mula.da.hl.ldinc(i32 immarg, i8*, i32 immarg, i32) nounwind +declare void @llvm.xtensa.mula.da.hh.ldinc(i32 immarg, i8*, i32 immarg, i32) nounwind +declare void @llvm.xtensa.mula.dd.ll.ldinc(i32 immarg, i8*, i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mula.dd.lh.ldinc(i32 immarg, i8*, i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mula.dd.hl.ldinc(i32 immarg, i8*, i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.mula.dd.hh.ldinc(i32 immarg, i8*, i32 immarg, i32 immarg) nounwind +declare void @llvm.xtensa.lddec(i32 immarg, i8*) nounwind +declare void @llvm.xtensa.ldinc(i32 immarg, i8*) nounwind +declare i32 @llvm.xtensa.rsr.acclo() nounwind +declare i32 @llvm.xtensa.rsr.acchi() nounwind +declare i32 @llvm.xtensa.rsr.m0() nounwind +declare i32 @llvm.xtensa.rsr.m1() nounwind +declare i32 @llvm.xtensa.rsr.m2() nounwind +declare i32 @llvm.xtensa.rsr.m3() nounwind +declare void @llvm.xtensa.xsr.acclo(i8*) nounwind +declare void @llvm.xtensa.xsr.acchi(i8*) nounwind +declare void @llvm.xtensa.xsr.m0(i8*) nounwind +declare void @llvm.xtensa.xsr.m1(i8*) nounwind +declare void @llvm.xtensa.xsr.m2(i8*) nounwind +declare void @llvm.xtensa.xsr.m3(i8*) nounwind +declare void @llvm.xtensa.wsr.acclo(i32) nounwind +declare void @llvm.xtensa.wsr.acchi(i32) nounwind +declare void @llvm.xtensa.wsr.m0(i32) nounwind +declare void @llvm.xtensa.wsr.m1(i32) nounwind +declare void @llvm.xtensa.wsr.m2(i32) nounwind +declare void @llvm.xtensa.wsr.m3(i32) nounwind + diff --git a/llvm/test/CodeGen/Xtensa/psram_memw.ll b/llvm/test/CodeGen/Xtensa/psram_memw.ll new file mode 100644 index 0000000000000..f10417ec779e6 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/psram_memw.ll @@ -0,0 +1,50 @@ +; RUN: llc -O1 -mtriple=xtensa -mcpu=esp32 -mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-strategy=memw %s -o - | FileCheck %s + +@a = dso_local local_unnamed_addr global i32 0, align 4 +@b = dso_local local_unnamed_addr global i32 0, align 4 + +; Function Attrs: nofree norecurse nounwind +define dso_local void @f(i32 %a1.coerce, i32 %a2.coerce, i32 %a3.coerce, i32 %a4.coerce, i32 %cond) local_unnamed_addr #0 { +entry: + %coerce.val.ip = inttoptr i32 %a1.coerce to i8* + %coerce.val.ip1 = inttoptr i32 %a2.coerce to i16* + %coerce.val.ip2 = inttoptr i32 %a3.coerce to i32* + %coerce.val.ip3 = inttoptr i32 %a4.coerce to i32* + %0 = load i32, i32* %coerce.val.ip2, align 4 + %conv = trunc i32 %0 to i8 + store i8 %conv, i8* %coerce.val.ip, align 1 + %tobool.not = icmp eq i32 %cond, 0 + br i1 %tobool.not, label %if.end, label %if.then +; CHECK: s8i a8, a2, 0 +; CHECK: memw + +if.then: ; preds = %entry + %1 = load i32, i32* %coerce.val.ip2, align 4 + %conv8 = trunc i32 %1 to i16 + store i16 %conv8, i16* %coerce.val.ip1, align 2 + %2 = load i32, i32* %coerce.val.ip3, align 4 + store i32 %2, i32* %coerce.val.ip2, align 4 + %conv10 = trunc i32 %2 to i8 + store i8 %conv10, i8* %coerce.val.ip, align 1 + br label %return +; CHECK: l32i.n a8, a4, 0 +; CHECK: s16i a8, a3, 0 +; CHECK: memw +; CHECK: memw +; CHECK: l32i.n a8, a5, 0 +; CHECK: s32i.n a8, a4, 0 +; CHECK: s8i a8, a2, 0 +; CHECK: memw + +if.end: ; preds = %entry + %3 = load i32, i32* %coerce.val.ip3, align 4 + %conv9 = trunc i32 %3 to i16 + store i16 %conv9, i16* %coerce.val.ip1, align 2 + br label %return +; CHECK: l32i.n a8, a5, 0 +; CHECK: s16i a8, a3, 0 +; CHECK: memw + +return: ; preds = %if.then, %if.end + ret void +} diff --git a/llvm/test/CodeGen/Xtensa/psram_nops.ll b/llvm/test/CodeGen/Xtensa/psram_nops.ll new file mode 100644 index 0000000000000..ece7d6f6432c4 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/psram_nops.ll @@ -0,0 +1,60 @@ +; RUN: llc -O1 -mtriple=xtensa -mcpu=esp32 -mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-strategy=nops %s -o - | FileCheck %s + +@a = dso_local local_unnamed_addr global i32 0, align 4 +@b = dso_local local_unnamed_addr global i32 0, align 4 + +; Function Attrs: nofree norecurse nounwind +define dso_local void @f(i32 %a1.coerce, i32 %a2.coerce, i32 %a3.coerce, i32 %a4.coerce, i32 %cond) local_unnamed_addr #0 { +entry: + %coerce.val.ip = inttoptr i32 %a1.coerce to i8* + %coerce.val.ip1 = inttoptr i32 %a2.coerce to i16* + %coerce.val.ip2 = inttoptr i32 %a3.coerce to i32* + %coerce.val.ip3 = inttoptr i32 %a4.coerce to i32* + %0 = load i32, i32* %coerce.val.ip2, align 4 + %conv = trunc i32 %0 to i8 + store i8 %conv, i8* %coerce.val.ip, align 1 + %tobool.not = icmp eq i32 %cond, 0 + br i1 %tobool.not, label %if.end, label %if.then +; CHECK: l32i.n a8, a4, 0 +; CHECK: memw +; CHECK: s8i a8, a2, 0 + + +if.then: ; preds = %entry + %1 = load i32, i32* %coerce.val.ip2, align 4 + %conv8 = trunc i32 %1 to i16 + store i16 %conv8, i16* %coerce.val.ip1, align 2 + %2 = load i32, i32* %coerce.val.ip3, align 4 + store i32 %2, i32* %coerce.val.ip2, align 4 + %conv10 = trunc i32 %2 to i8 + store i8 %conv10, i8* %coerce.val.ip, align 1 + br label %return +; CHECK: nop +; CHECK: nop +; CHECK: nop +; CHECK: nop +; CHECK: l32i.n a8, a4, 0 +; CHECK: s16i a8, a3, 0 +; CHECK: memw +; CHECK: nop +; CHECK: nop +; CHECK: nop +; CHECK: nop +; CHECK: l32i.n a8, a5, 0 +; CHECK: s32i.n a8, a4, 0 +; CHECK: memw +; CHECK: s8i a8, a2, 0 + +if.end: ; preds = %entry + %3 = load i32, i32* %coerce.val.ip3, align 4 + %conv9 = trunc i32 %3 to i16 + store i16 %conv9, i16* %coerce.val.ip1, align 2 + br label %return +; CHECK: l32i.n a8, a5, 0 +; CHECK: memw +; CHECK: s16i a8, a3, 0 + + +return: ; preds = %if.then, %if.end + ret void +} diff --git a/llvm/test/CodeGen/Xtensa/xtensa-fcmp.ll b/llvm/test/CodeGen/Xtensa/xtensa-fcmp.ll new file mode 100644 index 0000000000000..ffd4977a03c61 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/xtensa-fcmp.ll @@ -0,0 +1,18 @@ +; RUN: llc -O1 -mtriple=xtensa -mcpu=esp32 %s -o - | FileCheck %s + +define void @test_fcmp(i32 %x.coerce) { +; CHECK-LABEL: @test_fcmp +entry: + %0 = bitcast i32 %x.coerce to float + %cmp = fcmp oeq float %0, 0x7FF0000000000000 + br i1 %cmp, label %if.then, label %if.else +; CHECK: oeq.s b0, f9, f8 +; CHECK: bf b0, .LBB0_2 + +if.then: ; preds = %entry + unreachable + +if.else: ; preds = %entry + unreachable +} + diff --git a/llvm/test/CodeGen/Xtensa/xtensa-icmp.ll b/llvm/test/CodeGen/Xtensa/xtensa-icmp.ll new file mode 100644 index 0000000000000..684ff3b2b60b8 --- /dev/null +++ b/llvm/test/CodeGen/Xtensa/xtensa-icmp.ll @@ -0,0 +1,17 @@ +; RUN: llc -O1 -mtriple=xtensa -mcpu=esp32 %s -o - | FileCheck %s + +define i8 @test_bit(i8 %a) { +; CHECK-LABEL: @test_bit + %b = and i8 %a, 16 + %bool = icmp eq i8 %b, 0 + br i1 %bool, label %true, label %false +; CHECK: movi.n a8, 16 +; CHECK: and a8, a2, a8 +; CHECK: bnez a8, .LBB0_2 + +true: + ret i8 1 + +false: + ret i8 0 +} diff --git a/llvm/test/MC/Xtensa/directive-literal.s b/llvm/test/MC/Xtensa/directive-literal.s new file mode 100644 index 0000000000000..cc2fccdb61c89 --- /dev/null +++ b/llvm/test/MC/Xtensa/directive-literal.s @@ -0,0 +1,23 @@ +# RUN: llvm-mc -triple xtensa-esp-elf -filetype obj -o - %s \ +# RUN: | llvm-readobj -S --sd - \ +# RUN: | FileCheck %s + + .text + .literal_position + .literal .LCPI0_0, 305419896 + .global test_literal + .p2align 2 + .type test_literal,@function +test_literal: +# %bb.0: + entry a1, 32 + mov.n a7, a1 + l32r a2, .LCPI0_0 + retw.n + +# CHECK: Section { +# CHECK: Name: .literal.text +# CHECK: SectionData ( +# CHECK: 0000: 78563412 +# CHECK: ) +# CHECK: } \ No newline at end of file diff --git a/llvm/test/MC/Xtensa/directive-region.s b/llvm/test/MC/Xtensa/directive-region.s new file mode 100644 index 0000000000000..ca62571c31221 --- /dev/null +++ b/llvm/test/MC/Xtensa/directive-region.s @@ -0,0 +1,25 @@ +# RUN: llvm-mc -triple xtensa-esp-elf -filetype obj -o - %s \ +# RUN: | llvm-readobj -S --sd - \ +# RUN: | FileCheck %s + + .text + .begin literal_prefix .ExceptionVector + .literal_position + .literal .LCPI0_0, 305419896 + .global test_literal + .p2align 2 + .type test_literal,@function +test_literal: +# %bb.0: + entry a1, 32 + mov.n a7, a1 + l32r a2, .LCPI0_0 + retw.n + .end literal_prefix + +# CHECK: Section { +# CHECK: Name: .ExceptionVector.literal +# CHECK: SectionData ( +# CHECK: 0000: 78563412 +# CHECK: ) +# CHECK: } \ No newline at end of file diff --git a/llvm/test/MC/Xtensa/elf-header.s b/llvm/test/MC/Xtensa/elf-header.s new file mode 100644 index 0000000000000..00afa15d7a98a --- /dev/null +++ b/llvm/test/MC/Xtensa/elf-header.s @@ -0,0 +1,31 @@ +# RUN: llvm-mc %s -filetype=obj -triple=xtensa | llvm-readobj -h - \ +# RUN: | FileCheck -check-prefix=Xtensa %s + +# Xtensa: Format: elf32-xtensa +# Xtensa: Arch: xtensa +# Xtensa: AddressSize: 32bit +# Xtensa: ElfHeader { +# Xtensa: Ident { +# Xtensa: Magic: (7F 45 4C 46) +# Xtensa: Class: 32-bit (0x1) +# Xtensa: DataEncoding: LittleEndian (0x1) +# Xtensa: FileVersion: 1 +# Xtensa: OS/ABI: SystemV (0x0) +# Xtensa: ABIVersion: 0 +# Xtensa: Unused: (00 00 00 00 00 00 00) +# Xtensa: } +# Xtensa: Type: Relocatable (0x1) +# Xtensa: Machine: EM_XTENSA (0x5E) +# Xtensa: Version: 1 +# Xtensa: Entry: 0x0 +# Xtensa: ProgramHeaderOffset: 0x0 +# Xtensa: SectionHeaderOffset: 0x5C +# Xtensa: Flags [ (0x0) +# Xtensa: ] +# Xtensa: HeaderSize: 52 +# Xtensa: ProgramHeaderEntrySize: 0 +# Xtensa: ProgramHeaderCount: 0 +# Xtensa: SectionHeaderEntrySize: 40 +# Xtensa: SectionHeaderCount: 4 +# Xtensa: StringTableSectionIndex: 1 +# Xtensa: } diff --git a/llvm/test/MC/Xtensa/fixups-diagnostics.s b/llvm/test/MC/Xtensa/fixups-diagnostics.s new file mode 100644 index 0000000000000..d0d7b4d0f8857 --- /dev/null +++ b/llvm/test/MC/Xtensa/fixups-diagnostics.s @@ -0,0 +1,14 @@ +# RUN: not llvm-mc -triple xtensa -filetype obj < %s -o /dev/null 2>&1 | FileCheck %s + + .align 4 + + beq a0, a1, LBL1 # CHECK: :[[@LINE]]:3: error: branch 8-bit fixup value out of range +LBL0: + beqz a0, LBL2 # CHECK: :[[@LINE]]:3: error: branch 12-bit fixup value out of range + + call0 LBL0 # CHECK: :[[@LINE]]:3: error: fixup value must be 4-byte aligned + + .space 1<<8 +LBL1: + .space 1<<12 +LBL2: diff --git a/llvm/test/MC/Xtensa/fixups.s b/llvm/test/MC/Xtensa/fixups.s new file mode 100644 index 0000000000000..cd76f2a23322d --- /dev/null +++ b/llvm/test/MC/Xtensa/fixups.s @@ -0,0 +1,54 @@ +# RUN: llvm-mc -triple xtensa < %s -show-encoding \ +# RUN: | FileCheck -check-prefix=CHECK-FIXUP %s +# RUN: llvm-mc -filetype=obj -triple xtensa < %s \ +# RUN: | llvm-objdump -d - | FileCheck -check-prefix=CHECK-INSTR %s + + +# Checks that fixups that can be resolved within the same object file are +# applied correctly +.align 4 +LBL0: + +.fill 12 + +beq a0, a1, LBL0 +# CHECK-FIXUP: fixup A - offset: 0, value: LBL0, kind: fixup_xtensa_branch_8 +# CHECK-INSTR: beq a0, a1, . -12 + +beq a0, a1, LBL1 +# CHECK-FIXUP: fixup A - offset: 0, value: LBL1, kind: fixup_xtensa_branch_8 +# CHECK-INSTR: beq a0, a1, . +24 + +beqz a2, LBL0 +# CHECK-FIXUP: fixup A - offset: 0, value: LBL0, kind: fixup_xtensa_branch_12 +# CHECK-INSTR: beqz a2, . -18 + +beqz a2, LBL1 +# CHECK-FIXUP: fixup A - offset: 0, value: LBL1, kind: fixup_xtensa_branch_12 +# CHECK-INSTR: beqz a2, . +18 + +call0 LBL0 +# CHECK-FIXUP: fixup A - offset: 0, value: LBL0, kind: fixup_xtensa_call_18 +# CHECK-INSTR: call0 . -24 + +call0 LBL2 +# CHECK-FIXUP: fixup A - offset: 0, value: LBL2, kind: fixup_xtensa_call_18 +# CHECK-INSTR: call0 . +2056 + +j LBL0 +# CHECK-FIXUP: fixup A - offset: 0, value: LBL0, kind: fixup_xtensa_jump_18 +# CHECK-INSTR: j . -30 + +j LBL2 +# CHECK-FIXUP: fixup A - offset: 0, value: LBL2, kind: fixup_xtensa_jump_18 +# CHECK-INSTR: j . +2047 + +l32r a1, LBL0 +# CHECK-FIXUP: fixup A - offset: 0, value: LBL0, kind: fixup_xtensa_l32r_16 +# CHECK-INSTR: l32r a1, . -36 + +LBL1: + +.fill 2041 + +LBL2: diff --git a/llvm/test/MC/Xtensa/lit.local.cfg b/llvm/test/MC/Xtensa/lit.local.cfg new file mode 100644 index 0000000000000..9e0be9979305d --- /dev/null +++ b/llvm/test/MC/Xtensa/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'Xtensa' in config.root.targets: + config.unsupported = True diff --git a/llvm/test/MC/Xtensa/relocations.s b/llvm/test/MC/Xtensa/relocations.s new file mode 100644 index 0000000000000..19c2e16352509 --- /dev/null +++ b/llvm/test/MC/Xtensa/relocations.s @@ -0,0 +1,177 @@ +# RUN: llvm-mc -triple xtensa < %s -show-encoding \ +# RUN: | FileCheck -check-prefix=INSTR -check-prefix=FIXUP %s +# RUN: llvm-mc -filetype=obj -triple xtensa < %s \ +# RUN: | llvm-readobj -r - | FileCheck -check-prefix=RELOC %s + +# Check prefixes: +# RELOC - Check the relocation in the object. +# FIXUP - Check the fixup on the instruction. +# INSTR - Check the instruction is handled properly by the ASMPrinter + +.long func +# RELOC: R_XTENSA_32 func + +ball a1, a3, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: ball a1, a3, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bany a8, a13, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bany a8, a13, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bbc a8, a7, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bbc a8, a7, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bbci a3, 16, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bbci a3, 16, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bbs a12, a5, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bbs a12, a5, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bbsi a3, 16, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bbsi a3, 16, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bnall a7, a3, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bnall a7, a3, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bnone a2, a4, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bnone a2, a4, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +beq a1, a2, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: beq a1, a2, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +beq a11, a5, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: beq a11, a5, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +beqi a1, 256, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: beqi a1, 256, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +beqi a11, -1, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: beqi a11, -1, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +beqz a8, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: beqz a8, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_12 + +bge a14, a2, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bge a14, a2, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bgei a11, -1, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bgei a11, -1, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bgei a11, 128, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bgei a11, 128, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bgeu a14, a2, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bgeu a14, a2, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bgeui a9, 32768, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bgeui a9, 32768, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bgeui a7, 65536, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bgeui a7, 65536, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bgeui a7, 64, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bgeui a7, 64, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bgez a8, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bgez a8, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_12 + +blt a14, a2, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: blt a14, a2, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +blti a12, -1, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: blti a12, -1, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +blti a0, 32, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: blti a0, 32, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bgeu a13, a1, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bgeu a13, a1, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bltui a7, 16, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bltui a7, 16, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bltz a6, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bltz a6, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_12 + +bne a3, a4, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bne a3, a4, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bnei a5, 12, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bnei a5, 12, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_8 + +bnez a5, func +# RELOC: R_XTENSA_SLOT0_OP +# INST: bnez a5, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_12 + +call0 func +# RELOC: R_XTENSA_SLOT0_OP +# INST: call0 func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_call_18 + +j func +# RELOC: R_XTENSA_SLOT0_OP +# INSTR: j func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_jump_18 + +l32r a6, func +# RELOC: R_XTENSA_SLOT0_OP +# INSTR: l32r a6, func +# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_l32r_16 diff --git a/llvm/test/MC/Xtensa/xtensa-arith.s b/llvm/test/MC/Xtensa/xtensa-arith.s new file mode 100644 index 0000000000000..927cab3615a58 --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-arith.s @@ -0,0 +1,85 @@ +# RUN: llvm-mc %s -triple=xtensa -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + +.align 4 +LBL0: + +# Instruction format RRR +# CHECK-INST: abs a5, a6 +# CHECK: encoding: [0x60,0x51,0x60] +abs a5, a6 + +# Instruction format RRR +# CHECK-INST: add a3, a9, a4 +# CHECK: encoding: [0x40,0x39,0x80] +add a3, a9, a4 + +# Instruction format RRR +# CHECK-INST: add a15, a9, a1 +# CHECK: encoding: [0x10,0xf9,0x80] +add a15, a9, sp + +# Instruction format RRI8 +# CHECK-INST: addi a8, a1, -128 +# CHECK: encoding: [0x82,0xc1,0x80] +addi a8, sp, -128 + +# Instruction format RRI8 +# CHECK-INST: addi a8, a1, -12 +# CHECK: encoding: [0x82,0xc1,0xf4] +addi a8, a1, -12 + +# Instruction format RRI8 +# CHECK-INST: addmi a1, a2, 32512 +# CHECK: encoding: [0x12,0xd2,0x7f] +addmi a1, a2, 32512 + +# Instruction format RRR +# CHECK-INST: addx2 a2, a1, a5 +# CHECK: encoding: [0x50,0x21,0x90] +addx2 a2, sp, a5 + +# Instruction format RRR +# CHECK-INST: addx4 a3, a1, a6 +# CHECK: encoding: [0x60,0x31,0xa0] +addx4 a3, sp, a6 + +# Instruction format RRR +# CHECK-INST: addx8 a4, a1, a7 +# CHECK: encoding: [0x70,0x41,0xb0] +addx8 a4, sp, a7 + +# Instruction format RRR +# CHECK-INST: neg a1, a3 +# CHECK: encoding: [0x30,0x10,0x60] +neg a1, a3 + +# Instruction format RRR +# CHECK-INST: or a4, a5, a6 +# CHECK: encoding: [0x60,0x45,0x20] +or a4, a5, a6 + +# Instruction format RRR +# CHECK-INST: sub a8, a2, a1 +# CHECK: encoding: [0x10,0x82,0xc0] +sub a8, a2, a1 + +# Instruction format RRR +# CHECK-INST: subx2 a2, a1, a5 +# CHECK: encoding: [0x50,0x21,0xd0] +subx2 a2, sp, a5 + +# Instruction format RRR +# CHECK-INST: subx4 a3, a1, a6 +# CHECK: encoding: [0x60,0x31,0xe0] +subx4 a3, sp, a6 + +# Instruction format RRR +# CHECK-INST: subx8 a4, a1, a7 +# CHECK: encoding: [0x70,0x41,0xf0] +subx8 a4, sp, a7 + +# Instruction format RRR +# CHECK-INST: xor a6, a4, a5 +# CHECK: encoding: [0x50,0x64,0x30] +xor a6, a4, a5 diff --git a/llvm/test/MC/Xtensa/xtensa-branch.s b/llvm/test/MC/Xtensa/xtensa-branch.s new file mode 100644 index 0000000000000..eec16985d5566 --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-branch.s @@ -0,0 +1,171 @@ +# RUN: llvm-mc %s -triple=xtensa -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + + +.align 4 +LBL0: + +# Instruction format RRI8 +# CHECK-INST: ball a1, a3, LBL0 +# CHECK: encoding: [0x37,0x41,A] +ball a1, a3, LBL0 + +# Instruction format RRI8 +# CHECK-INST: bany a8, a13, LBL0 +# CHECK: encoding: [0xd7,0x88,A] +bany a8, a13, LBL0 + +# Instruction format RRI8 +# CHECK-INST: bbc a8, a7, LBL0 +# CHECK: encoding: [0x77,0x58,A] +bbc a8, a7, LBL0 + +# Instruction format RRI8 +# CHECK-INST: bbci a3, 16, LBL0 +# CHECK: encoding: [0x07,0x73,A] +bbci a3, 16, LBL0 + +# Instruction format RRI8 +# CHECK-INST: bbci a3, 16, LBL0 +# CHECK: encoding: [0x07,0x73,A] +bbci a3, (16), LBL0 + +# Instruction format RRI8 +# CHECK-INST: bbci a3, 16, LBL0 +# CHECK: encoding: [0x07,0x73,A] +bbci.l a3, 16, LBL0 + +# Instruction format RRI8 +# CHECK-INST: bbs a12, a5, LBL0 +# CHECK: encoding: [0x57,0xdc,A] +bbs a12, a5, LBL0 + +# Instruction format RRI8 +# CHECK-INST: bbsi a3, 16, LBL0 +# CHECK: encoding: [0x07,0xf3,A] +bbsi a3, 16, LBL0 + +# Instruction format RRI8 +# CHECK-INST: bbsi a3, 16, LBL0 +# CHECK: encoding: [0x07,0xf3,A] +bbsi.l a3, 16, LBL0 + +# Instruction format RRI8 +# CHECK-INST: bnall a7, a3, LBL0 +# CHECK: encoding: [0x37,0xc7,A] +bnall a7, a3, LBL0 + +# Instruction format RRI8 +# CHECK-INST: bnone a2, a4, LBL0 +# CHECK: encoding: [0x47,0x02,A] +bnone a2, a4, LBL0 + +# Instruction format RRI8 +# CHECK-INST: beq a1, a2, LBL0 +# CHECK: encoding: [0x27,0x11,A] +beq a1, a2, LBL0 + +# Instruction format RRI8 +# CHECK-INST: beq a11, a5, LBL0 +# CHECK: encoding: [0x57,0x1b,A] +beq a11, a5, LBL0 + +# Instruction format BRI8 +# CHECK-INST: beqi a1, 256, LBL0 +# CHECK: encoding: [0x26,0xf1,A] +beqi a1, 256, LBL0 + +# Instruction format RRI8 +# CHECK-INST: beqi a11, -1, LBL0 +# CHECK: encoding: [0x26,0x0b,A] +beqi a11, -1, LBL0 + +# Instruction format BRI12 +# CHECK-INST: beqz a8, LBL0 +# CHECK: encoding: [0x16,0bAAAA1000,A] +beqz a8, LBL0 + +# Instruction format RRI8 +# CHECK-INST: bge a14, a2, LBL0 +# CHECK: encoding: [0x27,0xae,A] +bge a14, a2, LBL0 + +# Instruction format BRI8 +# CHECK-INST: bgei a11, -1, LBL0 +# CHECK: encoding: [0xe6,0x0b,A] +bgei a11, -1, LBL0 + +# Instruction format BRI8 +# CHECK-INST: bgei a11, 128, LBL0 +# CHECK: encoding: [0xe6,0xeb,A] +bgei a11, 128, LBL0 + +# Instruction format RRI8 +# CHECK-INST: bgeu a14, a2, LBL0 +# CHECK: encoding: [0x27,0xbe,A] +bgeu a14, a2, LBL0 + +# Instruction format RRI8 +# CHECK-INST: bgeu a13, a1, LBL0 +# CHECK: encoding: [0x17,0xbd,A] +bgeu a13, a1, LBL0 + +# Instruction format BRI8 +# CHECK-INST: bgeui a9, 32768, LBL0 +# CHECK: encoding: [0xf6,0x09,A] +bgeui a9, 32768, LBL0 + +# Instruction format BRI8 +# CHECK-INST: bgeui a7, 65536, LBL0 +# CHECK: encoding: [0xf6,0x17,A] +bgeui a7, 65536, LBL0 + +# Instruction format BRI8 +# CHECK-INST: bgeui a7, 64, LBL0 +# CHECK: encoding: [0xf6,0xd7,A] +bgeui a7, 64, LBL0 + +# Instruction format BRI12 +# CHECK-INST: bgez a8, LBL0 +# CHECK: encoding: [0xd6,0bAAAA1000,A] +bgez a8, LBL0 + +# Instruction format RRI8 +# CHECK-INST: blt a14, a2, LBL0 +# CHECK: encoding: [0x27,0x2e,A] +blt a14, a2, LBL0 + +# Instruction format BRI8 +# CHECK-INST: blti a12, -1, LBL0 +# CHECK: encoding: [0xa6,0x0c,A] +blti a12, -1, LBL0 + +# Instruction format BRI8 +# CHECK-INST: blti a0, 32, LBL0 +# CHECK: encoding: [0xa6,0xc0,A] +blti a0, 32, LBL0 + +# Instruction format BRI8 +# CHECK-INST: bltui a7, 16, LBL0 +# CHECK: encoding: [0xb6,0xb7,A] +bltui a7, 16, LBL0 + +# Instruction format BRI12 +# CHECK-INST: bltz a6, LBL0 +# CHECK: encoding: [0x96,0bAAAA0110,A] +bltz a6, LBL0 + +# Instruction format RRI8 +# CHECK-INST: bne a3, a4, LBL0 +# CHECK: encoding: [0x47,0x93,A] +bne a3, a4, LBL0 + +# Instruction format BRI8 +# CHECK-INST: bnei a5, 12, LBL0 +# CHECK: encoding: [0x66,0xa5,A] +bnei a5, 12, LBL0 + +# Instruction format BRI12 +# CHECK-INST: bnez a5, LBL0 +# CHECK: encoding: [0x56,0bAAAA0101,A] +bnez a5, LBL0 diff --git a/llvm/test/MC/Xtensa/xtensa-call-jump.s b/llvm/test/MC/Xtensa/xtensa-call-jump.s new file mode 100644 index 0000000000000..5b4c95d64b633 --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-call-jump.s @@ -0,0 +1,35 @@ +# RUN: llvm-mc %s -triple=xtensa -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + + +.align 4 +LBL0: + +# Instruction format CALL +# CHECK-INST: call0 LBL0 +# CHECK: encoding: [0bAA000101,A,A] +call0 LBL0 + +# Instruction format CALLX +# CHECK-INST: callx0 a1 +# CHECK: encoding: [0xc0,0x01,0x00] +callx0 a1 + +# Instruction format CALL +# CHECK-INST: j LBL0 +# CHECK: encoding: [0bAA000110,A,A] +j LBL0 + +# Instruction format CALLX +# CHECK-INST: jx a2 +# CHECK: encoding: [0xa0,0x02,0x00] +jx a2 + +# CHECK-INST: ill +# CHECK: encoding: [0x00,0x00,0x00] +ill + +# Instruction format CALLX +# CHECK-INST: ret +# CHECK: encoding: [0x80,0x00,0x00] +ret diff --git a/llvm/test/MC/Xtensa/xtensa-esp32s2-valid.s b/llvm/test/MC/Xtensa/xtensa-esp32s2-valid.s new file mode 100644 index 0000000000000..9c998e919c81e --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-esp32s2-valid.s @@ -0,0 +1,21 @@ +# RUN: llvm-mc %s -triple=xtensa -mattr=+esp32s2 -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + +.align 4 +LBL0: + +# CHECK-INST: clr_bit_gpio_out 52 +# CHECK: encoding: [0x40,0x03,0x06] +clr_bit_gpio_out 52 + +# CHECK-INST: get_gpio_in a2 +# CHECK: encoding: [0x20,0x30,0x06] +get_gpio_in a2 + +# CHECK-INST: set_bit_gpio_out 18 +# CHECK: encoding: [0x20,0x11,0x06] +set_bit_gpio_out 18 + +# CHECK-INST: wr_mask_gpio_out a3, a2 +# CHECK: encoding: [0x20,0x23,0x06] +wr_mask_gpio_out a3, a2 diff --git a/llvm/test/MC/Xtensa/xtensa-esp32s3-valid.s b/llvm/test/MC/Xtensa/xtensa-esp32s3-valid.s new file mode 100644 index 0000000000000..50037ea38df15 --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-esp32s3-valid.s @@ -0,0 +1,21 @@ +# RUN: llvm-mc %s -triple=xtensa -mattr=+esp32s3 -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + +.align 4 +LBL0: + +# CHECK-INST: ee.clr_bit_gpio_out 52 +# CHECK: encoding: [0x44,0x43,0x76] +ee.clr_bit_gpio_out 52 + +# CHECK-INST: ee.get_gpio_in a2 +# CHECK: encoding: [0x24,0x08,0x65] +ee.get_gpio_in a2 + +# CHECK-INST: ee.set_bit_gpio_out 18 +# CHECK: encoding: [0x24,0x41,0x75] +ee.set_bit_gpio_out 18 + +# CHECK-INST: ee.wr_mask_gpio_out a3, a2 +# CHECK: encoding: [0x34,0x42,0x72] +ee.wr_mask_gpio_out a3, a2 diff --git a/llvm/test/MC/Xtensa/xtensa-invalid.s b/llvm/test/MC/Xtensa/xtensa-invalid.s new file mode 100644 index 0000000000000..f5ad98d29a81e --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-invalid.s @@ -0,0 +1,98 @@ +# RUN: not llvm-mc -triple xtensa < %s 2>&1 | FileCheck %s + +LBL0: + +# Out of range immediates + +# imm8 +addi a1, a2, 33000 +# CHECK: error: expected immediate in range [-32896, 32639] + +# imm8 +addi a1, a2, -33000 +# CHECK: error: expected immediate in range [-32896, 32639] + +# imm1_16 +extui a1, a2, 5, 17 +# CHECK: error: expected immediate in range [1, 16] + +# imm8_sh8 +addmi a1, a2, 33 +# CHECK: error: expected immediate in range [-32768, 32512], first 8 bits should be zero + +# shimm1_31 +slli a1, a2, 0 +# CHECK: error: expected immediate in range [1, 31] + +# uimm4 +srli a1, a2, 16 +# CHECK: error: expected immediate in range [0, 15] + +# uimm5 +ssai 32 +# CHECK: error: expected immediate in range [0, 31] + +# imm64n_4n +ssai 32 +# CHECK: error: expected immediate in range [0, 31] + +# offset8m8 +s8i a1, a2, 300 +# CHECK: error: expected immediate in range [0, 255] + +# offset16m8 +l16si a1, a2, 512 +# CHECK: error: expected immediate in range [0, 510], first bit should be zero + +# offset32m8 +l32i a1, a2, 1024 +# CHECK: error: expected immediate in range [0, 1020], first 2 bits should be zero + +# b4const +beqi a1, 257, LBL0 +# CHECK: error: expected b4const immediate + +# b4constu +bgeui a9, 32000, LBL0 +# CHECK: error: expected b4constu immediate + +# Invalid number of operands +addi a1, a2 # CHECK: :[[@LINE]]:1: error: too few operands for instruction +addi a1, a2, 4, 4 # CHECK: :[[@LINE]]:17: error: invalid operand for instruction + +# Invalid mnemonics +aaa a10, a12 # CHECK: :[[@LINE]]:1: error: unrecognized instruction mnemonic + +# Invalid operand types +and sp, a2, 10 # CHECK: :[[@LINE]]:13: error: invalid operand for instruction +addi sp, a1, a2 # CHECK: :[[@LINE]]:14: error: expected immediate in range [-32896, 32639] + +# Check invalid register names for different formats +# Instruction format RRR +or r2, sp, a3 # CHECK: :[[@LINE]]:4: error: invalid operand for instruction +and a1, r10, a3 # CHECK: :[[@LINE]]:9: error: invalid operand for instruction +sub a1, sp, a100 # CHECK: :[[@LINE]]:13: error: invalid operand for instruction +# Instruction format RRI8 +addi a101, sp, 10 # CHECK: :[[@LINE]]:6: error: invalid operand for instruction +addi a1, r10, 10 # CHECK: :[[@LINE]]:10: error: invalid operand for instruction +# Instruction format RSR +wsr.uregister a2 # CHECK: :[[@LINE]]:1: error: invalid register name +wsr a2, uregister # CHECK: :[[@LINE]]:9: error: invalid operand for instruction +# Instruction format BRI12 +beqz b1, LBL0 # CHECK: :[[@LINE]]:6: error: invalid operand for instruction +# Instruction format BRI8 +bltui r7, 16, LBL0 # CHECK: :[[@LINE]]:7: error: invalid operand for instruction +# Instruction format CALLX +callx0 r10 # CHECK: :[[@LINE]]:8: error: invalid operand for instruction + +# Check invalid operands order for different formats +# Instruction format RRI8 +addi a1, 10, a2 # CHECK: :[[@LINE]]:10: error: invalid operand for instruction +# Instruction format RSR +wsr sar, a2 # CHECK: :[[@LINE]]:5: error: invalid operand for instruction +# Instruction format BRI12 +beqz LBL0, a2 # CHECK: :[[@LINE]]:6: error: invalid operand for instruction +# Instruction format BRI8 +bltui 16, a7, LBL0 # CHECK: :[[@LINE]]:7: error: invalid operand for instruction +bltui a7, LBL0, 16 # CHECK: :[[@LINE]]:20: error: unknown operand + diff --git a/llvm/test/MC/Xtensa/xtensa-memorder.s b/llvm/test/MC/Xtensa/xtensa-memorder.s new file mode 100644 index 0000000000000..bf1ebac159b2d --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-memorder.s @@ -0,0 +1,16 @@ +# RUN: llvm-mc %s -triple=xtensa -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + + +.align 4 +LBL0: + +# Instruction format RRR +# CHECK-INST: extw +# CHECK: encoding: [0xd0,0x20,0x00] +extw + +# Instruction format RRR +# CHECK-INST: memw +# CHECK: encoding: [0xc0,0x20,0x00] +memw diff --git a/llvm/test/MC/Xtensa/xtensa-memory.s b/llvm/test/MC/Xtensa/xtensa-memory.s new file mode 100644 index 0000000000000..889e156c95916 --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-memory.s @@ -0,0 +1,45 @@ +# RUN: llvm-mc %s -triple=xtensa -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + + +.align 4 +LBL0: + +# Instruction format RRI8 +# CHECK-INST: l8ui a2, a1, 3 +# CHECK: encoding: [0x22,0x01,0x03] +l8ui a2, sp, 3 + +# Instruction format RRI8 +# CHECK-INST: l16si a3, a1, 4 +# CHECK: encoding: [0x32,0x91,0x02] +l16si a3, sp, 4 + +# Instruction format RRI8 +# CHECK-INST: l16ui a4, a1, 6 +# CHECK: encoding: [0x42,0x11,0x03] +l16ui a4, sp, 6 + +# Instruction format RRI8 +# CHECK-INST: l32i a5, a1, 8 +# CHECK: encoding: [0x52,0x21,0x02] +l32i a5, sp, 8 + +# CHECK-INST: l32r a6, LBL0 +# CHECK: encoding: [0x61,A,A] +l32r a6, LBL0 + +# Instruction format RRI8 +# CHECK-INST: s8i a2, a1, 3 +# CHECK: encoding: [0x22,0x41,0x03] +s8i a2, sp, 3 + +# Instruction format RRI8 +# CHECK-INST: s16i a3, a1, 4 +# CHECK: encoding: [0x32,0x51,0x02] +s16i a3, sp, 4 + +# Instruction format RRI8 +# CHECK-INST: s32i a5, a1, 8 +# CHECK: encoding: [0x52,0x61,0x02] +s32i a5, sp, 8 diff --git a/llvm/test/MC/Xtensa/xtensa-move.s b/llvm/test/MC/Xtensa/xtensa-move.s new file mode 100644 index 0000000000000..87616599709c3 --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-move.s @@ -0,0 +1,31 @@ +# RUN: llvm-mc %s -triple=xtensa -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + + +.align 4 +LBL0: + +# Instruction format RRR +# CHECK-INST: moveqz a2, a3, a4 +# CHECK: encoding: [0x40,0x23,0x83] +moveqz a2, a3, a4 + +# Instruction format RRR +# CHECK-INST: movgez a3, a11, a12 +# CHECK: encoding: [0xc0,0x3b,0xb3] +movgez a3, a11, a12 + +# Instruction format RRI8 +# CHECK-INST: movi a1, -2048 +# CHECK: encoding: [0x12,0xa8,0x00] +movi a1, -2048 + +# Instruction format RRR +# CHECK-INST: movltz a7, a8, a9 +# CHECK: encoding: [0x90,0x78,0xa3] +movltz a7, a8, a9 + +# Instruction format RRR +# CHECK-INST: movnez a10, a11, a12 +# CHECK: encoding: [0xc0,0xab,0x93] +movnez a10, a11, a12 diff --git a/llvm/test/MC/Xtensa/xtensa-processor-control.s b/llvm/test/MC/Xtensa/xtensa-processor-control.s new file mode 100644 index 0000000000000..72e08f2e36697 --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-processor-control.s @@ -0,0 +1,86 @@ +# RUN: llvm-mc %s -triple=xtensa -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + + +.align 4 +LBL0: + +# Instruction format RRR +# CHECK-INST: dsync +# CHECK: encoding: [0x30,0x20,0x00] +dsync + +# Instruction format RRR +# CHECK-INST: esync +# CHECK: encoding: [0x20,0x20,0x00] +esync + +# Instruction format RRR +# CHECK-INST: isync +# CHECK: encoding: [0x00,0x20,0x00] +isync + +# Instruction format RRR +# CHECK-INST: nop +# CHECK: encoding: [0xf0,0x20,0x00] +nop + +# Instruction format RSR +# CHECK-INST: rsr a8, sar +# CHECK: encoding: [0x80,0x03,0x03] +rsr a8, sar + +# Instruction format RSR +# CHECK-INST: rsr a8, sar +# CHECK: encoding: [0x80,0x03,0x03] +rsr.sar a8 + +# Instruction format RSR +# CHECK-INST: rsr a8, sar +# CHECK: encoding: [0x80,0x03,0x03] +rsr a8, 3 + +# Instruction format RRR +# CHECK-INST: rer a3, a4 +# CHECK: encoding: [0x30,0x64,0x40] +rer a3, a4 + +# Instruction format RRR +# CHECK-INST: rsync +# CHECK: encoding: [0x10,0x20,0x00] +rsync + +# Instruction format RRR +# CHECK-INST: wer a3, a4 +# CHECK: encoding: [0x30,0x74,0x40] +wer a3, a4 + +# Instruction format RSR +# CHECK-INST: wsr a8, sar +# CHECK: encoding: [0x80,0x03,0x13] +wsr a8, sar + +# Instruction format RSR +# CHECK-INST: wsr a8, sar +# CHECK: encoding: [0x80,0x03,0x13] +wsr.sar a8 + +# Instruction format RSR +# CHECK-INST: wsr a8, sar +# CHECK: encoding: [0x80,0x03,0x13] +wsr a8, 3 + +# Instruction format RRR +# CHECK-INST: xsr a8, sar +# CHECK: encoding: [0x80,0x03,0x61] +xsr a8, sar + +# Instruction format RRR +# CHECK-INST: xsr a8, sar +# CHECK: encoding: [0x80,0x03,0x61] +xsr.sar a8 + +# Instruction format RRR +# CHECK-INST: xsr a8, sar +# CHECK: encoding: [0x80,0x03,0x61] +xsr a8, 3 diff --git a/llvm/test/MC/Xtensa/xtensa-shift.s b/llvm/test/MC/Xtensa/xtensa-shift.s new file mode 100644 index 0000000000000..3f9c980ff5554 --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-shift.s @@ -0,0 +1,66 @@ +# RUN: llvm-mc %s -triple=xtensa -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + + +.align 4 +LBL0: + +# Instruction format RRR +# CHECK-INST: extui a1, a2, 7, 8 +# CHECK: encoding: [0x20,0x17,0x74] +extui a1, a2, 7, 8 + +# Instruction format RRR +# CHECK-INST: sll a10, a11 +# CHECK: encoding: [0x00,0xab,0xa1] +sll a10, a11 + +# Instruction format RRR +# CHECK-INST: slli a5, a1, 15 +# CHECK: encoding: [0x10,0x51,0x11] +slli a5, a1, 15 + +# Instruction format RRR +# CHECK-INST: sra a12, a3 +# CHECK: encoding: [0x30,0xc0,0xb1] +sra a12, a3 + +# Instruction format RRR +# CHECK-INST: srai a8, a5, 0 +# CHECK: encoding: [0x50,0x80,0x21] +srai a8, a5, 0 + +# Instruction format RRR +# CHECK-INST: src a3, a4, a5 +# CHECK: encoding: [0x50,0x34,0x81] +src a3, a4, a5 + +# Instruction format RRR +# CHECK-INST: srl a6, a7 +# CHECK: encoding: [0x70,0x60,0x91] +srl a6, a7 + +# Instruction format RRR +# CHECK-INST: srli a3, a4, 8 +# CHECK: encoding: [0x40,0x38,0x41] +srli a3, a4, 8 + +# Instruction format RRR +# CHECK-INST: ssa8l a14 +# CHECK: encoding: [0x00,0x2e,0x40] +ssa8l a14 + +# Instruction format RRR +# CHECK-INST: ssai 31 +# CHECK: encoding: [0x10,0x4f,0x40] +ssai 31 + +# Instruction format RRR +# CHECK-INST: ssl a0 +# CHECK: encoding: [0x00,0x10,0x40] +ssl a0 + +# Instruction format RRR +# CHECK-INST: ssr a2 +# CHECK: encoding: [0x00,0x02,0x40] +ssr a2 diff --git a/llvm/test/MC/Xtensa/xtensa-valid-dbg.s b/llvm/test/MC/Xtensa/xtensa-valid-dbg.s new file mode 100644 index 0000000000000..9391c60e43f69 --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-valid-dbg.s @@ -0,0 +1,9 @@ +# RUN: llvm-mc %s -triple=xtensa -mattr=+debug -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + +.align 4 +LBL0: + +# CHECK-INST: break 1, 2 +# CHECK: encoding: [0x20,0x41,0x00] + break 1, 2 diff --git a/llvm/test/MC/Xtensa/xtensa-valid-density.s b/llvm/test/MC/Xtensa/xtensa-valid-density.s new file mode 100644 index 0000000000000..860597dd62627 --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-valid-density.s @@ -0,0 +1,30 @@ +# RUN: llvm-mc %s -triple=xtensa -mattr=+density -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + +.align 4 +LBL0: + +# Instruction format RRRN +# CHECK-INST: ill.n +# CHECK: encoding: [0x6d,0xf0] +ill.n + +# Instruction format RRRN +# CHECK-INST: add.n a3, a9, a4 +# CHECK: encoding: [0x4a,0x39] +add.n a3, a9, a4 + +# Instruction format RRRN +# CHECK-INST: addi.n a8, a1, 12 +# CHECK: encoding: [0xcb,0x81] +addi.n a8, a1, 12 + +# Instruction format RRRN +# CHECK-INST: l32i.n a5, a1, 8 +# CHECK: encoding: [0x58,0x21] +l32i.n a5, sp, 8 + +# Instruction format RRRN +# CHECK-INSt: s32i.n a5, a1, 56 +# CHECK: encoding: [0x59,0xe1] +s32i.n a5, sp, 56 diff --git a/llvm/test/MC/Xtensa/xtensa-valid-exc.s b/llvm/test/MC/Xtensa/xtensa-valid-exc.s new file mode 100644 index 0000000000000..4d1e9198bd9ad --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-valid-exc.s @@ -0,0 +1,21 @@ +# RUN: llvm-mc %s -triple=xtensa -mattr=+exception -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + +.align 4 +LBL0: + +# CHECK-INST: excw +# CHECK: encoding: [0x80,0x20,0x00] + excw + +# CHECK-INST: rfde +# CHECK: encoding: [0x00,0x32,0x00] + rfde + +# CHECK-INST: rfe +# CHECK: encoding: [0x00,0x30,0x00] + rfe + +# CHECK-INST: syscall +# CHECK: encoding: [0x00,0x50,0x00] + syscall diff --git a/llvm/test/MC/Xtensa/xtensa-valid-float.s b/llvm/test/MC/Xtensa/xtensa-valid-float.s new file mode 100644 index 0000000000000..08fe0abad999e --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-valid-float.s @@ -0,0 +1,179 @@ +# RUN: llvm-mc %s -triple=xtensa -mattr=+fp -mattr=+bool -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + +.align 4 +LBL0: + +# Instruction format RRR +# CHECK-INST: abs.s f2, f3 +# CHECK: encoding: [0x10,0x23,0xfa] + abs.s f2, f3 +# CHECK-INST: add.s f2, f3, f4 +# CHECK: encoding: [0x40,0x23,0x0a] + add.s f2, f3, f4 +# CHECK-INST: addexp.s f2, f3 +# CHECK: encoding: [0xe0,0x23,0xfa] + addexp.s f2, f3 +# CHECK-INST: addexpm.s f2, f3 +# CHECK: encoding: [0xf0,0x23,0xfa] + addexpm.s f2, f3 + +# CHECK-INST: ceil.s a2, f3, 5 +# CHECK: encoding: [0x50,0x23,0xba] + ceil.s a2, f3, 5 +# CHECK-INST: const.s f3, 5 +# CHECK: encoding: [0x30,0x35,0xfa] + const.s f3, 5 + +# CHECK-INST: div0.s f2, f3 +# CHECK: encoding: [0x70,0x23,0xfa] + div0.s f2, f3 +# CHECK-INST: divn.s f2, f3, f4 +# CHECK: encoding: [0x40,0x23,0x7a] + divn.s f2, f3, f4 + +# CHECK-INST: float.s f2, a3, 5 +# CHECK: encoding: [0x50,0x23,0xca] + float.s f2, a3, 5 +# CHECK-INST: floor.s a2, f3, 5 +# CHECK: encoding: [0x50,0x23,0xaa] + floor.s a2, f3, 5 + +# CHECK-INST: lsi f2, a3, 8 +# CHECK: encoding: [0x23,0x03,0x02] + lsi f2, a3, 8 +# CHECK-INST: lsip f2, a3, 8 +# CHECK: encoding: [0x23,0x83,0x02] + lsip f2, a3, 8 +# CHECK-INST: lsx f2, a3, a4 +# CHECK: encoding: [0x40,0x23,0x08] + lsx f2, a3, a4 +# CHECK-INST: lsxp f2, a3, a4 +# CHECK: encoding: [0x40,0x23,0x18] + lsxp f2, a3, a4 + +# CHECK-INST: madd.s f2, f3, f4 +# CHECK: encoding: [0x40,0x23,0x4a] + madd.s f2, f3, f4 +# CHECK-INST: maddn.s f2, f3, f4 +# CHECK: encoding: [0x40,0x23,0x6a] + maddn.s f2, f3, f4 +# CHECK-INST: mkdadj.s f2, f3 +# CHECK: encoding: [0xd0,0x23,0xfa] + mkdadj.s f2, f3 +# CHECK-INST: mksadj.s f2, f3 +# CHECK: encoding: [0xc0,0x23,0xfa] + mksadj.s f2, f3 + +# CHECK-INST: mov.s f2, f3 +# CHECK: encoding: [0x00,0x23,0xfa] + mov.s f2, f3 + +# CHECK-INST: moveqz.s f2, f3, a4 +# CHECK: encoding: [0x40,0x23,0x8b] + moveqz.s f2, f3, a4 +# CHECK-INST: movf.s f2, f3, b0 +# CHECK: encoding: [0x00,0x23,0xcb] + movf.s f2, f3, b0 +# CHECK-INST: movgez.s f2, f3, a4 +# CHECK: encoding: [0x40,0x23,0xbb] + movgez.s f2, f3, a4 +# CHECK-INST: movltz.s f2, f3, a4 +# CHECK: encoding: [0x40,0x23,0xab] + movltz.s f2, f3, a4 +# CHECK-INST: movnez.s f2, f3, a4 +# CHECK: encoding: [0x40,0x23,0x9b] + movnez.s f2, f3, a4 +# CHECK-INST: movt.s f2, f3, b0 +# CHECK: encoding: [0x00,0x23,0xdb] + movt.s f2, f3, b0 + +# CHECK-INST: msub.s f2, f3, f4 +# CHECK: encoding: [0x40,0x23,0x5a] + msub.s f2, f3, f4 +# CHECK-INST: mul.s f2, f3, f4 +# CHECK: encoding: [0x40,0x23,0x2a] + mul.s f2, f3, f4 +# CHECK-INST: neg.s f2, f3 +# CHECK: encoding: [0x60,0x23,0xfa] + neg.s f2, f3 + +# CHECK-INST: nexp01.s f2, f3 +# CHECK: encoding: [0xb0,0x23,0xfa] + nexp01.s f2, f3 + +# CHECK-INST: oeq.s b0, f2, f3 +# CHECK: encoding: [0x30,0x02,0x2b] + oeq.s b0, f2, f3 +# CHECK-INST: ole.s b0, f2, f3 +# CHECK: encoding: [0x30,0x02,0x6b] + ole.s b0, f2, f3 +# CHECK-INST: olt.s b0, f2, f3 +# CHECK: encoding: [0x30,0x02,0x4b] + olt.s b0, f2, f3 + +# CHECK-INST: recip0.s f2, f3 +# CHECK: encoding: [0x80,0x23,0xfa] + recip0.s f2, f3 + +# CHECK-INST: rfr a2, f3 +# CHECK: encoding: [0x40,0x23,0xfa] + rfr a2, f3 + +# CHECK-INST: round.s a2, f3, 5 +# CHECK: encoding: [0x50,0x23,0x8a] + round.s a2, f3, 5 +# CHECK-INST: rsqrt0.s f2, f3 +# CHECK: encoding: [0xa0,0x23,0xfa] + rsqrt0.s f2, f3 +# CHECK-INST: sqrt0.s f2, f3 +# CHECK: encoding: [0x90,0x23,0xfa] + sqrt0.s f2, f3 + +# CHECK-INST: ssi f2, a3, 8 +# CHECK: encoding: [0x23,0x43,0x02] + ssi f2, a3, 8 +# CHECK-INST: ssip f2, a3, 8 +# CHECK: encoding: [0x23,0xc3,0x02] + ssip f2, a3, 8 +# CHECK-INST: ssx f2, a3, a4 +# CHECK: encoding: [0x40,0x23,0x48] + ssx f2, a3, a4 +# CHECK-INST: ssxp f2, a3, a4 +# CHECK: encoding: [0x40,0x23,0x58] + ssxp f2, a3, a4 + +# CHECK-INST: sub.s f2, f3, f4 +# CHECK: encoding: [0x40,0x23,0x1a] + sub.s f2, f3, f4 + +# CHECK-INST: trunc.s a2, f3, 5 +# CHECK: encoding: [0x50,0x23,0x9a] + trunc.s a2, f3, 5 + +# CHECK-INST: ueq.s b0, f2, f3 +# CHECK: encoding: [0x30,0x02,0x3b] + ueq.s b0, f2, f3 + +# CHECK-INST: ufloat.s f2, a3, 5 +# CHECK: encoding: [0x50,0x23,0xda] + ufloat.s f2, a3, 5 + +# CHECK-INST: ule.s b0, f2, f3 +# CHECK: encoding: [0x30,0x02,0x7b] + ule.s b0, f2, f3 +# CHECK-INST: ult.s b0, f2, f3 +# CHECK: encoding: [0x30,0x02,0x5b] + ult.s b0, f2, f3 +# CHECK-INST: un.s b0, f2, f3 +# CHECK: encoding: [0x30,0x02,0x1b] + un.s b0, f2, f3 + +# CHECK-INST: utrunc.s a2, f3, 5 +# CHECK: encoding: [0x50,0x23,0xea] + utrunc.s a2, f3, 5 + +# CHECK-INST: wfr f2, a3 +# CHECK: encoding: [0x50,0x23,0xfa] + wfr f2, a3 + diff --git a/llvm/test/MC/Xtensa/xtensa-valid-int.s b/llvm/test/MC/Xtensa/xtensa-valid-int.s new file mode 100644 index 0000000000000..08309d299f7b3 --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-valid-int.s @@ -0,0 +1,19 @@ +# RUN: llvm-mc %s -triple=xtensa -mattr=+interrupt -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + + +.align 4 +LBL0: + +# Instruction format RRR +# CHECK-INST: rfi 1 +# CHECK: encoding: [0x10,0x31,0x00] + rfi 1 + +# CHECK-INST: rsil a3, 1 +# CHECK: encoding: [0x30,0x61,0x00] + rsil a3, 1 + +# CHECK-INST: waiti 1 +# CHECK: encoding: [0x00,0x71,0x00] + waiti 1 diff --git a/llvm/test/MC/Xtensa/xtensa-valid-mac16.s b/llvm/test/MC/Xtensa/xtensa-valid-mac16.s new file mode 100644 index 0000000000000..2c5170b75652c --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-valid-mac16.s @@ -0,0 +1,235 @@ +# RUN: llvm-mc %s -triple=xtensa -mattr=+mac16 -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + +.align 4 +LBL0: + +# Instruction format RRR +# CHECK-INST: umul.aa.ll a2, a3 +# CHECK: encoding: [0x34,0x02,0x70] + umul.aa.ll a2, a3 +# CHECK-INST: umul.aa.lh a2, a3 +# CHECK: encoding: [0x34,0x02,0x72] + umul.aa.lh a2, a3 +# CHECK-INST: umul.aa.hl a2, a3 +# CHECK: encoding: [0x34,0x02,0x71] + umul.aa.hl a2, a3 +# CHECK-INST: umul.aa.hh a2, a3 +# CHECK: encoding: [0x34,0x02,0x73] + umul.aa.hh a2, a3 + +# CHECK-INST: mul.aa.ll a2, a3 +# CHECK: encoding: [0x34,0x02,0x74] + mul.aa.ll a2, a3 +# CHECK-INST: mul.aa.lh a2, a3 +# CHECK: encoding: [0x34,0x02,0x76] + mul.aa.lh a2, a3 +# CHECK-INST: mul.aa.hl a2, a3 +# CHECK: encoding: [0x34,0x02,0x75] + mul.aa.hl a2, a3 +# CHECK-INST: mul.aa.hh a2, a3 +# CHECK: encoding: [0x34,0x02,0x77] + mul.aa.hh a2, a3 + +# CHECK-INST: mul.ad.ll a2, m2 +# CHECK: encoding: [0x04,0x02,0x34] + mul.ad.ll a2, m2 +# CHECK-INST: mul.ad.lh a2, m2 +# CHECK: encoding: [0x04,0x02,0x36] + mul.ad.lh a2, m2 +# CHECK-INST: mul.ad.hl a2, m2 +# CHECK: encoding: [0x04,0x02,0x35] + mul.ad.hl a2, m2 +# CHECK-INST: mul.ad.hh a2, m2 +# CHECK: encoding: [0x04,0x02,0x37] + mul.ad.hh a2, m2 + +# CHECK-INST: mul.da.ll m1, a3 +# CHECK: encoding: [0x34,0x40,0x64] + mul.da.ll m1, a3 +# CHECK-INST: mul.da.lh m1, a3 +# CHECK: encoding: [0x34,0x40,0x66] + mul.da.lh m1, a3 +# CHECK-INST: mul.da.hl m1, a3 +# CHECK: encoding: [0x34,0x40,0x65] + mul.da.hl m1, a3 +# CHECK-INST: mul.da.hh m1, a3 +# CHECK: encoding: [0x34,0x40,0x67] + mul.da.hh m1, a3 + +# CHECK-INST: mul.dd.ll m1, m2 +# CHECK: encoding: [0x04,0x40,0x24] + mul.dd.ll m1, m2 +# CHECK-INST: mul.dd.lh m1, m2 +# CHECK: encoding: [0x04,0x40,0x26] + mul.dd.lh m1, m2 +# CHECK-INST: mul.dd.hl m1, m2 +# CHECK: encoding: [0x04,0x40,0x25] + mul.dd.hl m1, m2 +# CHECK-INST: mul.dd.hh m1, m2 +# CHECK: encoding: [0x04,0x40,0x27] + mul.dd.hh m1, m2 + +# CHECK-INST: mula.aa.ll a2, a3 +# CHECK: encoding: [0x34,0x02,0x78] + mula.aa.ll a2, a3 +# CHECK-INST: mula.aa.lh a2, a3 +# CHECK: encoding: [0x34,0x02,0x7a] + mula.aa.lh a2, a3 +# CHECK-INST: mula.aa.hl a2, a3 +# CHECK: encoding: [0x34,0x02,0x79] + mula.aa.hl a2, a3 +# CHECK-INST: mula.aa.hh a2, a3 +# CHECK: encoding: [0x34,0x02,0x7b] + mula.aa.hh a2, a3 + +# CHECK-INST: mula.ad.ll a2, m2 +# CHECK: encoding: [0x04,0x02,0x38] + mula.ad.ll a2, m2 +# CHECK-INST: mula.ad.lh a2, m2 +# CHECK: encoding: [0x04,0x02,0x3a] + mula.ad.lh a2, m2 +# CHECK-INST: mula.ad.hl a2, m2 +# CHECK: encoding: [0x04,0x02,0x39] + mula.ad.hl a2, m2 +# CHECK-INST: mula.ad.hh a2, m2 +# CHECK: encoding: [0x04,0x02,0x3b] + mula.ad.hh a2, m2 + +# CHECK-INST: mula.da.ll m1, a3 +# CHECK: encoding: [0x34,0x40,0x68] + mula.da.ll m1, a3 +# CHECK-INST: mula.da.lh m1, a3 +# CHECK: encoding: [0x34,0x40,0x6a] + mula.da.lh m1, a3 +# CHECK-INST: mula.da.hl m1, a3 +# CHECK: encoding: [0x34,0x40,0x69] + mula.da.hl m1, a3 +# CHECK-INST: mula.da.hh m1, a3 +# CHECK: encoding: [0x34,0x40,0x6b] + mula.da.hh m1, a3 + +# CHECK-INST: mula.dd.ll m1, m2 +# CHECK: encoding: [0x04,0x40,0x28] + mula.dd.ll m1, m2 +# CHECK-INST: mula.dd.lh m1, m2 +# CHECK: encoding: [0x04,0x40,0x2a] + mula.dd.lh m1, m2 +# CHECK-INST: mula.dd.hl m1, m2 +# CHECK: encoding: [0x04,0x40,0x29] + mula.dd.hl m1, m2 +# CHECK-INST: mula.dd.hh m1, m2 +# CHECK: encoding: [0x04,0x40,0x2b] + mula.dd.hh m1, m2 + +# CHECK-INST: muls.aa.ll a2, a3 +# CHECK: encoding: [0x34,0x02,0x7c] + muls.aa.ll a2, a3 +# CHECK-INST: muls.aa.lh a2, a3 +# CHECK: encoding: [0x34,0x02,0x7e] + muls.aa.lh a2, a3 +# CHECK-INST: muls.aa.hl a2, a3 +# CHECK: encoding: [0x34,0x02,0x7d] + muls.aa.hl a2, a3 +# CHECK-INST: muls.aa.hh a2, a3 +# CHECK: encoding: [0x34,0x02,0x7f] + muls.aa.hh a2, a3 + +# CHECK-INST: muls.ad.ll a2, m2 +# CHECK: encoding: [0x04,0x02,0x3c] + muls.ad.ll a2, m2 +# CHECK-INST: muls.ad.lh a2, m2 +# CHECK: encoding: [0x04,0x02,0x3e] + muls.ad.lh a2, m2 +# CHECK-INST: muls.ad.hl a2, m2 +# CHECK: encoding: [0x04,0x02,0x3d] + muls.ad.hl a2, m2 +# CHECK-INST: muls.ad.hh a2, m2 +# CHECK: encoding: [0x04,0x02,0x3f] + muls.ad.hh a2, m2 + +# CHECK-INST: muls.da.ll m1, a3 +# CHECK: encoding: [0x34,0x40,0x6c] + muls.da.ll m1, a3 +# CHECK-INST: muls.da.lh m1, a3 +# CHECK: encoding: [0x34,0x40,0x6e] + muls.da.lh m1, a3 +# CHECK-INST: muls.da.hl m1, a3 +# CHECK: encoding: [0x34,0x40,0x6d] + muls.da.hl m1, a3 +# CHECK-INST: muls.da.hh m1, a3 +# CHECK: encoding: [0x34,0x40,0x6f] + muls.da.hh m1, a3 + +# CHECK-INST: muls.dd.ll m1, m2 +# CHECK: encoding: [0x04,0x40,0x2c] + muls.dd.ll m1, m2 +# CHECK-INST: muls.dd.lh m1, m2 +# CHECK: encoding: [0x04,0x40,0x2e] + muls.dd.lh m1, m2 +# CHECK-INST: muls.dd.hl m1, m2 +# CHECK: encoding: [0x04,0x40,0x2d] + muls.dd.hl m1, m2 +# CHECK-INST: muls.dd.hh m1, m2 +# CHECK: encoding: [0x04,0x40,0x2f] + muls.dd.hh m1, m2 + +# CHECK-INST: mula.da.ll.lddec m1, a8, m0, a3 +# CHECK: encoding: [0x34,0x18,0x58] + mula.da.ll.lddec m1, a8, m0, a3 +# CHECK-INST: mula.da.hl.lddec m1, a8, m0, a3 +# CHECK: encoding: [0x34,0x18,0x59] + mula.da.hl.lddec m1, a8, m0, a3 +# CHECK-INST: mula.da.lh.lddec m1, a8, m0, a3 +# CHECK: encoding: [0x34,0x18,0x5a] + mula.da.lh.lddec m1, a8, m0, a3 +# CHECK-INST: mula.da.hh.lddec m1, a8, m0, a3 +# CHECK: encoding: [0x34,0x18,0x5b] + mula.da.hh.lddec m1, a8, m0, a3 + +# CHECK-INST: mula.dd.ll.lddec m1, a8, m0, m2 +# CHECK: encoding: [0x04,0x18,0x18] + mula.dd.ll.lddec m1, a8, m0, m2 +# CHECK-INST: mula.dd.hl.lddec m1, a8, m0, m2 +# CHECK: encoding: [0x04,0x18,0x19] + mula.dd.hl.lddec m1, a8, m0, m2 +# CHECK-INST: mula.dd.lh.lddec m1, a8, m0, m2 +# CHECK: encoding: [0x04,0x18,0x1a] + mula.dd.lh.lddec m1, a8, m0, m2 +# CHECK-INST: mula.dd.hh.lddec m1, a8, m0, m2 +# CHECK: encoding: [0x04,0x18,0x1b] + mula.dd.hh.lddec m1, a8, m0, m2 + +# CHECK-INST: mula.da.ll.ldinc m1, a8, m0, a3 +# CHECK: encoding: [0x34,0x18,0x48] + mula.da.ll.ldinc m1, a8, m0, a3 +# CHECK-INST: mula.da.hl.ldinc m1, a8, m0, a3 +# CHECK: encoding: [0x34,0x18,0x49] + mula.da.hl.ldinc m1, a8, m0, a3 +# CHECK-INST: mula.da.lh.ldinc m1, a8, m0, a3 +# CHECK: encoding: [0x34,0x18,0x4a] + mula.da.lh.ldinc m1, a8, m0, a3 +# CHECK-INST: mula.da.hh.ldinc m1, a8, m0, a3 +# CHECK: encoding: [0x34,0x18,0x4b] + mula.da.hh.ldinc m1, a8, m0, a3 + +# CHECK-INST: mula.dd.ll.ldinc m1, a8, m0, m2 +# CHECK: encoding: [0x04,0x18,0x08] + mula.dd.ll.ldinc m1, a8, m0, m2 +# CHECK-INST: mula.dd.hl.ldinc m1, a8, m0, m2 +# CHECK: encoding: [0x04,0x18,0x09] + mula.dd.hl.ldinc m1, a8, m0, m2 +# CHECK-INST: mula.dd.lh.ldinc m1, a8, m0, m2 +# CHECK: encoding: [0x04,0x18,0x0a] + mula.dd.lh.ldinc m1, a8, m0, m2 +# CHECK-INST: mula.dd.hh.ldinc m1, a8, m0, m2 +# CHECK: encoding: [0x04,0x18,0x0b] + mula.dd.hh.ldinc m1, a8, m0, m2 + +# CHECK-INST: lddec m0, a8 +# CHECK: encoding: [0x04,0x08,0x90] + lddec m0, a8 +# CHECK-INST: ldinc m0, a8 +# CHECK: encoding: [0x04,0x08,0x80] + ldinc m0, a8 + diff --git a/llvm/test/MC/Xtensa/xtensa-valid-mul16.s b/llvm/test/MC/Xtensa/xtensa-valid-mul16.s new file mode 100644 index 0000000000000..472e1ddb533da --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-valid-mul16.s @@ -0,0 +1,14 @@ +# RUN: llvm-mc %s -triple=xtensa -mattr=+mul16 -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + +.align 4 +LBL0: + +# Instruction format RRR +# CHECK-INST: mul16s a2, a3, a4 +# CHECK: encoding: [0x40,0x23,0xd1] + mul16s a2, a3, a4 + +# CHECK-INST: mul16u a2, a3, a4 +# CHECK: encoding: [0x40,0x23,0xc1] + mul16u a2, a3, a4 diff --git a/llvm/test/MC/Xtensa/xtensa-valid-regprotect.s b/llvm/test/MC/Xtensa/xtensa-valid-regprotect.s new file mode 100644 index 0000000000000..cd1535498d648 --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-valid-regprotect.s @@ -0,0 +1,14 @@ +# RUN: llvm-mc %s -triple=xtensa -mattr=+regprotect -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + +.align 4 +LBL0: + +# Instruction format RRR +# CHECK-INST: wdtlb a3, a4 +# CHECK: encoding: [0x30,0xe4,0x50] + wdtlb a3, a4 + +# CHECK-INST: witlb a3, a4 +# CHECK: encoding: [0x30,0x64,0x50] + witlb a3, a4 diff --git a/llvm/test/MC/Xtensa/xtensa-valid-ur.s b/llvm/test/MC/Xtensa/xtensa-valid-ur.s new file mode 100644 index 0000000000000..81dca2f18acd5 --- /dev/null +++ b/llvm/test/MC/Xtensa/xtensa-valid-ur.s @@ -0,0 +1,20 @@ +# RUN: llvm-mc %s -triple=xtensa -mattr=+threadptr -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK,CHECK-INST %s + +.align 4 +LBL0: + +# Instruction format RSR +# CHECK-INST: rur a3, threadptr +# CHECK: encoding: [0x70,0x3e,0xe3] + rur a3, threadptr +# CHECK-INST: rur a3, threadptr +# CHECK: encoding: [0x70,0x3e,0xe3] + rur.threadptr a3 + +# CHECK-INST: wur a3, threadptr +# CHECK: encoding: [0x30,0xe7,0xf3] + wur a3, threadptr +# CHECK-INST: wur a3, threadptr +# CHECK: encoding: [0x30,0xe7,0xf3] + wur.threadptr a3 diff --git a/llvm/test/Object/obj2yaml.test b/llvm/test/Object/obj2yaml.test index 05860471e9126..f07172595173f 100644 --- a/llvm/test/Object/obj2yaml.test +++ b/llvm/test/Object/obj2yaml.test @@ -547,7 +547,7 @@ # ELF-MIPS64EL-NEXT: Binding: STB_GLOBAL # ELF-MIPS64EL-NEXT: ... -# RUN: yaml2obj %s -o %t-x86-64 +# RUN: yaml2obj --docnum=1 %s -o %t-x86-64 # RUN: obj2yaml %t-x86-64 | FileCheck %s --check-prefix ELF-X86-64 # ELF-X86-64: FileHeader: @@ -678,6 +678,25 @@ Symbols: - Name: puts Binding: STB_GLOBAL +# RUN: yaml2obj --docnum=2 %s -o %t-xtensa +# RUN: obj2yaml %t-xtensa | FileCheck %s --check-prefix ELF-XTENSA + +# ELF-XTENSA: FileHeader: +# ELF-XTENSA-NEXT: Class: ELFCLASS32 +# ELF-XTENSA-NEXT: Data: ELFDATA2LSB +# ELF-XTENSA-NEXT: Type: ET_EXEC +# ELF-XTENSA-NEXT: Machine: EM_XTENSA +## As EF_XTENSA_MACH_NONE == 0, it is always printed by obj2yaml. +# ELF-XTENSA-NEXT: Flags: [ EF_XTENSA_XT_INSN, EF_XTENSA_MACH_NONE, EF_XTENSA_XT_LIT ] + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_XTENSA + Flags: [ EF_XTENSA_XT_INSN, EF_XTENSA_XT_LIT ] + # RUN: obj2yaml %p/Inputs/trivial-object-test.elf-avr | FileCheck %s --check-prefix ELF-AVR # ELF-AVR: FileHeader: diff --git a/llvm/test/tools/llvm-readobj/ELF/reloc-types-xtensa.test b/llvm/test/tools/llvm-readobj/ELF/reloc-types-xtensa.test new file mode 100644 index 0000000000000..0356e4f53d48f --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/reloc-types-xtensa.test @@ -0,0 +1,182 @@ +## Test that llvm-readobj shows proper relocation type +## names and values for the Xtensa target. + +# RUN: yaml2obj %s -o %t-xtensa.o +# RUN: llvm-readobj -r %t-xtensa.o | FileCheck %s + +# CHECK: 0x0 R_XTENSA_NONE - 0x0 +# CHECK-NEXT: 0x4 R_XTENSA_32 - 0x0 +# CHECK-NEXT: 0x8 R_XTENSA_RTLD - 0x0 +# CHECK-NEXT: 0xC R_XTENSA_GLOB_DAT - 0x0 +# CHECK-NEXT: 0x10 R_XTENSA_JMP_SLOT - 0x0 +# CHECK-NEXT: 0x14 R_XTENSA_RELATIVE - 0x0 +# CHECK-NEXT: 0x18 R_XTENSA_PLT - 0x0 +# CHECK-NEXT: 0x1C R_XTENSA_OP0 - 0x0 +# CHECK-NEXT: 0x20 R_XTENSA_OP1 - 0x0 +# CHECK-NEXT: 0x24 R_XTENSA_OP2 - 0x0 +# CHECK-NEXT: 0x28 R_XTENSA_ASM_EXPAND - 0x0 +# CHECK-NEXT: 0x2C R_XTENSA_ASM_SIMPLIFY - 0x0 +# CHECK-NEXT: 0x30 R_XTENSA_32_PCREL - 0x0 +# CHECK-NEXT: 0x34 R_XTENSA_GNU_VTINHERIT - 0x0 +# CHECK-NEXT: 0x38 R_XTENSA_GNU_VTENTRY - 0x0 +# CHECK-NEXT: 0x3C R_XTENSA_DIFF8 - 0x0 +# CHECK-NEXT: 0x40 R_XTENSA_DIFF16 - 0x0 +# CHECK-NEXT: 0x44 R_XTENSA_DIFF32 - 0x0 +# CHECK-NEXT: 0x48 R_XTENSA_SLOT0_OP - 0x0 +# CHECK-NEXT: 0x4C R_XTENSA_SLOT1_OP - 0x0 +# CHECK-NEXT: 0x50 R_XTENSA_SLOT2_OP - 0x0 +# CHECK-NEXT: 0x54 R_XTENSA_SLOT3_OP - 0x0 +# CHECK-NEXT: 0x58 R_XTENSA_SLOT4_OP - 0x0 +# CHECK-NEXT: 0x5C R_XTENSA_SLOT5_OP - 0x0 +# CHECK-NEXT: 0x60 R_XTENSA_SLOT6_OP - 0x0 +# CHECK-NEXT: 0x64 R_XTENSA_SLOT7_OP - 0x0 +# CHECK-NEXT: 0x68 R_XTENSA_SLOT8_OP - 0x0 +# CHECK-NEXT: 0x6C R_XTENSA_SLOT9_OP - 0x0 +# CHECK-NEXT: 0x70 R_XTENSA_SLOT10_OP - 0x0 +# CHECK-NEXT: 0x74 R_XTENSA_SLOT11_OP - 0x0 +# CHECK-NEXT: 0x78 R_XTENSA_SLOT12_OP - 0x0 +# CHECK-NEXT: 0x7C R_XTENSA_SLOT13_OP - 0x0 +# CHECK-NEXT: 0x80 R_XTENSA_SLOT14_OP - 0x0 +# CHECK-NEXT: 0x84 R_XTENSA_SLOT0_ALT - 0x0 +# CHECK-NEXT: 0x88 R_XTENSA_SLOT1_ALT - 0x0 +# CHECK-NEXT: 0x8C R_XTENSA_SLOT2_ALT - 0x0 +# CHECK-NEXT: 0x90 R_XTENSA_SLOT3_ALT - 0x0 +# CHECK-NEXT: 0x94 R_XTENSA_SLOT4_ALT - 0x0 +# CHECK-NEXT: 0x98 R_XTENSA_SLOT5_ALT - 0x0 +# CHECK-NEXT: 0x9C R_XTENSA_SLOT6_ALT - 0x0 +# CHECK-NEXT: 0xA0 R_XTENSA_SLOT7_ALT - 0x0 +# CHECK-NEXT: 0xA4 R_XTENSA_SLOT8_ALT - 0x0 +# CHECK-NEXT: 0xA8 R_XTENSA_SLOT9_ALT - 0x0 +# CHECK-NEXT: 0xAC R_XTENSA_SLOT10_ALT - 0x0 +# CHECK-NEXT: 0xB0 R_XTENSA_SLOT11_ALT - 0x0 +# CHECK-NEXT: 0xB4 R_XTENSA_SLOT12_ALT - 0x0 +# CHECK-NEXT: 0xB8 R_XTENSA_SLOT13_ALT - 0x0 +# CHECK-NEXT: 0xBC R_XTENSA_SLOT14_ALT - 0x0 +# CHECK-NEXT: 0xC0 R_XTENSA_TLSDESC_FN - 0x0 +# CHECK-NEXT: 0xC4 R_XTENSA_TLSDESC_ARG - 0x0 +# CHECK-NEXT: 0xC8 R_XTENSA_TLS_DTPOFF - 0x0 +# CHECK-NEXT: 0xCC R_XTENSA_TLS_TPOFF - 0x0 +# CHECK-NEXT: 0xD0 R_XTENSA_TLS_FUNC - 0x0 +# CHECK-NEXT: 0xD4 R_XTENSA_TLS_ARG - 0x0 +# CHECK-NEXT: 0xD8 R_XTENSA_TLS_CALL - 0x0 + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_XTENSA +Sections: + - Name: .rela.text + Type: SHT_RELA + Relocations: + - Offset: 0x0000000000000000 + Type: R_XTENSA_NONE + - Offset: 0x0000000000000004 + Type: R_XTENSA_32 + - Offset: 0x0000000000000008 + Type: R_XTENSA_RTLD + - Offset: 0x000000000000000C + Type: R_XTENSA_GLOB_DAT + - Offset: 0x0000000000000010 + Type: R_XTENSA_JMP_SLOT + - Offset: 0x0000000000000014 + Type: R_XTENSA_RELATIVE + - Offset: 0x0000000000000018 + Type: R_XTENSA_PLT + - Offset: 0x000000000000001C + Type: R_XTENSA_OP0 + - Offset: 0x0000000000000020 + Type: R_XTENSA_OP1 + - Offset: 0x0000000000000024 + Type: R_XTENSA_OP2 + - Offset: 0x0000000000000028 + Type: R_XTENSA_ASM_EXPAND + - Offset: 0x000000000000002C + Type: R_XTENSA_ASM_SIMPLIFY + - Offset: 0x0000000000000030 + Type: R_XTENSA_32_PCREL + - Offset: 0x0000000000000034 + Type: R_XTENSA_GNU_VTINHERIT + - Offset: 0x0000000000000038 + Type: R_XTENSA_GNU_VTENTRY + - Offset: 0x000000000000003C + Type: R_XTENSA_DIFF8 + - Offset: 0x0000000000000040 + Type: R_XTENSA_DIFF16 + - Offset: 0x0000000000000044 + Type: R_XTENSA_DIFF32 + - Offset: 0x0000000000000048 + Type: R_XTENSA_SLOT0_OP + - Offset: 0x000000000000004C + Type: R_XTENSA_SLOT1_OP + - Offset: 0x0000000000000050 + Type: R_XTENSA_SLOT2_OP + - Offset: 0x0000000000000054 + Type: R_XTENSA_SLOT3_OP + - Offset: 0x0000000000000058 + Type: R_XTENSA_SLOT4_OP + - Offset: 0x000000000000005C + Type: R_XTENSA_SLOT5_OP + - Offset: 0x0000000000000060 + Type: R_XTENSA_SLOT6_OP + - Offset: 0x0000000000000064 + Type: R_XTENSA_SLOT7_OP + - Offset: 0x0000000000000068 + Type: R_XTENSA_SLOT8_OP + - Offset: 0x000000000000006C + Type: R_XTENSA_SLOT9_OP + - Offset: 0x0000000000000070 + Type: R_XTENSA_SLOT10_OP + - Offset: 0x0000000000000074 + Type: R_XTENSA_SLOT11_OP + - Offset: 0x0000000000000078 + Type: R_XTENSA_SLOT12_OP + - Offset: 0x000000000000007C + Type: R_XTENSA_SLOT13_OP + - Offset: 0x0000000000000080 + Type: R_XTENSA_SLOT14_OP + - Offset: 0x0000000000000084 + Type: R_XTENSA_SLOT0_ALT + - Offset: 0x0000000000000088 + Type: R_XTENSA_SLOT1_ALT + - Offset: 0x000000000000008C + Type: R_XTENSA_SLOT2_ALT + - Offset: 0x0000000000000090 + Type: R_XTENSA_SLOT3_ALT + - Offset: 0x0000000000000094 + Type: R_XTENSA_SLOT4_ALT + - Offset: 0x0000000000000098 + Type: R_XTENSA_SLOT5_ALT + - Offset: 0x000000000000009C + Type: R_XTENSA_SLOT6_ALT + - Offset: 0x00000000000000A0 + Type: R_XTENSA_SLOT7_ALT + - Offset: 0x00000000000000A4 + Type: R_XTENSA_SLOT8_ALT + - Offset: 0x00000000000000A8 + Type: R_XTENSA_SLOT9_ALT + - Offset: 0x00000000000000AC + Type: R_XTENSA_SLOT10_ALT + - Offset: 0x00000000000000B0 + Type: R_XTENSA_SLOT11_ALT + - Offset: 0x00000000000000B4 + Type: R_XTENSA_SLOT12_ALT + - Offset: 0x00000000000000B8 + Type: R_XTENSA_SLOT13_ALT + - Offset: 0x00000000000000BC + Type: R_XTENSA_SLOT14_ALT + - Offset: 0x00000000000000C0 + Type: R_XTENSA_TLSDESC_FN + - Offset: 0x00000000000000C4 + Type: R_XTENSA_TLSDESC_ARG + - Offset: 0x00000000000000C8 + Type: R_XTENSA_TLS_DTPOFF + - Offset: 0x00000000000000CC + Type: R_XTENSA_TLS_TPOFF + - Offset: 0x00000000000000D0 + Type: R_XTENSA_TLS_FUNC + - Offset: 0x00000000000000D4 + Type: R_XTENSA_TLS_ARG + - Offset: 0x00000000000000D8 + Type: R_XTENSA_TLS_CALL diff --git a/llvm/test/tools/llvm-readobj/ELF/xtensa-header-flags.test b/llvm/test/tools/llvm-readobj/ELF/xtensa-header-flags.test new file mode 100644 index 0000000000000..a7934370940e3 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/xtensa-header-flags.test @@ -0,0 +1,20 @@ +## Check that we are able to dump EF_XTENSA_XT_* flags correctly + +# RUN: yaml2obj %s -o %t.insn -DFLAG=INSN +# RUN: llvm-readobj -S --file-headers %t.insn | FileCheck --check-prefixes=ALL,INSN %s + +# RUN: yaml2obj %s -o %t.lit -DFLAG=LIT +# RUN: llvm-readobj -S --file-headers %t.lit | FileCheck --check-prefixes=ALL,LIT %s + +# ALL: Flags [ +# INSN: EF_XTENSA_XT_INSN (0x100) +# LIT: EF_XTENSA_XT_LIT (0x200) +# ALL: ] + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_XTENSA + Flags: [ EF_XTENSA_XT_[[FLAG]] ] diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index ba7bae96ade36..addb9a226e900 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -1657,6 +1657,12 @@ const EnumEntry ElfHeaderLoongArchFlags[] = { ENUM_ENT(EF_LOONGARCH_BASE_ABI_LP64D, "LP64, DOUBLE-FLOAT"), }; +static const EnumEntry ElfHeaderXtensaFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, EF_XTENSA_MACH_NONE), + LLVM_READOBJ_ENUM_ENT(ELF, EF_XTENSA_XT_INSN), + LLVM_READOBJ_ENUM_ENT(ELF, EF_XTENSA_XT_LIT) +}; + const EnumEntry ElfSymOtherFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, STV_INTERNAL), @@ -3369,6 +3375,9 @@ template void GNUELFDumper::printFileHeaders() { else if (e.e_machine == EM_LOONGARCH) ElfFlags = printFlags(e.e_flags, makeArrayRef(ElfHeaderLoongArchFlags), unsigned(ELF::EF_LOONGARCH_BASE_ABI_MASK)); + else if (e.e_machine == EM_XTENSA) + ElfFlags = printFlags(e.e_flags, makeArrayRef(ElfHeaderXtensaFlags), + unsigned(ELF::EF_XTENSA_MACH)); Str = "0x" + utohexstr(e.e_flags); if (!ElfFlags.empty()) Str = Str + ", " + ElfFlags; @@ -6522,6 +6531,9 @@ template void LLVMELFDumper::printFileHeaders() { else if (E.e_machine == EM_LOONGARCH) W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderLoongArchFlags), unsigned(ELF::EF_LOONGARCH_BASE_ABI_MASK)); + else if (E.e_machine == EM_XTENSA) + W.printFlags("Flags", E.e_flags, makeArrayRef(ElfHeaderXtensaFlags), + unsigned(ELF::EF_XTENSA_MACH)); else W.printFlags("Flags", E.e_flags); W.printNumber("HeaderSize", E.e_ehsize); diff --git a/llvm/unittests/ADT/TripleTest.cpp b/llvm/unittests/ADT/TripleTest.cpp index 3b6a582c42fd1..55e2512cb91f9 100644 --- a/llvm/unittests/ADT/TripleTest.cpp +++ b/llvm/unittests/ADT/TripleTest.cpp @@ -790,6 +790,18 @@ TEST(TripleTest, ParsedIDs) { EXPECT_EQ(Triple::ShaderModel, T.getOS()); EXPECT_EQ(Triple::Amplification, T.getEnvironment()); + T = Triple("xtensa"); + EXPECT_EQ(Triple::xtensa, T.getArch()); + EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); + EXPECT_EQ(Triple::UnknownOS, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + + T = Triple("xtensa-unknown-unknown"); + EXPECT_EQ(Triple::xtensa, T.getArch()); + EXPECT_EQ(Triple::UnknownVendor, T.getVendor()); + EXPECT_EQ(Triple::UnknownOS, T.getOS()); + EXPECT_EQ(Triple::UnknownEnvironment, T.getEnvironment()); + T = Triple("huh"); EXPECT_EQ(Triple::UnknownArch, T.getArch()); } @@ -1151,6 +1163,11 @@ TEST(TripleTest, BitWidthPredicates) { EXPECT_TRUE(T.isArch32Bit()); EXPECT_FALSE(T.isArch64Bit()); EXPECT_TRUE(T.isDXIL()); + + T.setArch(Triple::xtensa); + EXPECT_FALSE(T.isArch16Bit()); + EXPECT_TRUE(T.isArch32Bit()); + EXPECT_FALSE(T.isArch64Bit()); } TEST(TripleTest, BitWidthArchVariants) { @@ -1345,6 +1362,10 @@ TEST(TripleTest, BitWidthArchVariants) { T.setArch(Triple::dxil); EXPECT_EQ(Triple::dxil, T.get32BitArchVariant().getArch()); EXPECT_EQ(Triple::UnknownArch, T.get64BitArchVariant().getArch()); + + T.setArch(Triple::xtensa); + EXPECT_EQ(Triple::xtensa, T.get32BitArchVariant().getArch()); + EXPECT_EQ(Triple::UnknownArch, T.get64BitArchVariant().getArch()); } TEST(TripleTest, EndianArchVariants) { diff --git a/llvm/unittests/Object/ELFObjectFileTest.cpp b/llvm/unittests/Object/ELFObjectFileTest.cpp index 877a140f969a7..25aa1f3b2606b 100644 --- a/llvm/unittests/Object/ELFObjectFileTest.cpp +++ b/llvm/unittests/Object/ELFObjectFileTest.cpp @@ -310,6 +310,14 @@ TEST(ELFObjectFileTest, MachineTestForCSKY) { checkFormatAndArch(D, Formats[I++], Triple::csky); } +TEST(ELFObjectFileTest, MachineTestForXtensa) { + std::array Formats = {"elf32-xtensa", "elf32-xtensa", + "elf64-unknown", "elf64-unknown"}; + size_t I = 0; + for (const DataForTest &D : generateData(ELF::EM_XTENSA)) + checkFormatAndArch(D, Formats[I++], Triple::xtensa); +} + // ELF relative relocation type test. TEST(ELFObjectFileTest, RelativeRelocationTypeTest) { EXPECT_EQ(ELF::R_CKCORE_RELATIVE, getELFRelativeRelocationType(ELF::EM_CSKY)); diff --git a/llvm/utils/gn/secondary/llvm/include/llvm/IR/BUILD.gn b/llvm/utils/gn/secondary/llvm/include/llvm/IR/BUILD.gn index ab051f43e6216..eeb55c23bf9a6 100644 --- a/llvm/utils/gn/secondary/llvm/include/llvm/IR/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/include/llvm/IR/BUILD.gn @@ -177,6 +177,16 @@ tablegen("IntrinsicsXCore") { td_file = "Intrinsics.td" } +tablegen("IntrinsicsXtensa") { + visibility = [ ":public_tablegen" ] + output_name = "IntrinsicsXtensa.h" + args = [ + "-gen-intrinsic-enums", + "-intrinsic-prefix=xtensa", + ] + td_file = "Intrinsics.td" +} + # Groups all tablegen() calls that create .inc files that are included in # IR's public headers. //llvm/lib/IR has this as a public_dep, so targets # depending on //llvm/lib/IR don't need to depend on this. This exists @@ -207,5 +217,6 @@ group("public_tablegen") { ":IntrinsicsWebAssembly", ":IntrinsicsX86", ":IntrinsicsXCore", + ":IntrinsicsXtensa", ] }